summaryrefslogtreecommitdiff
path: root/local.c
diff options
context:
space:
mode:
Diffstat (limited to 'local.c')
-rw-r--r--local.c2076
1 files changed, 0 insertions, 2076 deletions
diff --git a/local.c b/local.c
deleted file mode 100644
index d05afb6..0000000
--- a/local.c
+++ /dev/null
@@ -1,2076 +0,0 @@
-/*
- * libiio - Library for interfacing industrial I/O (IIO) devices
- *
- * Copyright (C) 2014 Analog Devices, Inc.
- * Author: Paul Cercueil <paul.cercueil@analog.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * */
-
-#include "debug.h"
-#include "iio-private.h"
-#include "sort.h"
-
-#include <dirent.h>
-#include <errno.h>
-#include <limits.h>
-#include <poll.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/eventfd.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/utsname.h>
-#include <time.h>
-#include <unistd.h>
-#include <fcntl.h>
-#ifdef WITH_LOCAL_CONFIG
-#include <ini.h>
-#endif
-
-#define DEFAULT_TIMEOUT_MS 1000
-
-#define NB_BLOCKS 4
-
-#define BLOCK_ALLOC_IOCTL _IOWR('i', 0xa0, struct block_alloc_req)
-#define BLOCK_FREE_IOCTL _IO('i', 0xa1)
-#define BLOCK_QUERY_IOCTL _IOWR('i', 0xa2, struct block)
-#define BLOCK_ENQUEUE_IOCTL _IOWR('i', 0xa3, struct block)
-#define BLOCK_DEQUEUE_IOCTL _IOWR('i', 0xa4, struct block)
-
-#define BLOCK_FLAG_CYCLIC BIT(1)
-
-/* Forward declarations */
-static ssize_t local_read_dev_attr(const struct iio_device *dev,
- const char *attr, char *dst, size_t len, enum iio_attr_type type);
-static ssize_t local_read_chn_attr(const struct iio_channel *chn,
- const char *attr, char *dst, size_t len);
-static ssize_t local_write_dev_attr(const struct iio_device *dev,
- const char *attr, const char *src, size_t len, enum iio_attr_type type);
-static ssize_t local_write_chn_attr(const struct iio_channel *chn,
- const char *attr, const char *src, size_t len);
-
-struct block_alloc_req {
- uint32_t type,
- size,
- count,
- id;
-};
-
-struct block {
- uint32_t id,
- size,
- bytes_used,
- type,
- flags,
- offset;
- uint64_t timestamp;
-};
-
-struct iio_context_pdata {
- unsigned int rw_timeout_ms;
-};
-
-struct iio_device_pdata {
- int fd;
- bool blocking;
- unsigned int samples_count;
- unsigned int max_nb_blocks;
- unsigned int allocated_nb_blocks;
-
- struct block *blocks;
- void **addrs;
- int last_dequeued;
- bool is_high_speed, cyclic, cyclic_buffer_enqueued, buffer_enabled;
-
- int cancel_fd;
-};
-
-struct iio_channel_pdata {
- char *enable_fn;
- struct iio_channel_attr *protected_attrs;
- unsigned int nb_protected_attrs;
-};
-
-static const char * const device_attrs_blacklist[] = {
- "dev",
- "uevent",
-};
-
-static const char * const buffer_attrs_reserved[] = {
- "length",
- "enable",
-};
-
-static int ioctl_nointr(int fd, unsigned long request, void *data)
-{
- int ret;
-
- do {
- ret = ioctl(fd, request, data);
- } while (ret == -1 && errno == EINTR);
-
- return ret;
-}
-
-static void local_free_channel_pdata(struct iio_channel *chn)
-{
- if (chn->pdata) {
- free(chn->pdata->enable_fn);
- free(chn->pdata);
- }
-}
-
-static void local_free_pdata(struct iio_device *device)
-{
- unsigned int i;
-
- for (i = 0; i < device->nb_channels; i++)
- local_free_channel_pdata(device->channels[i]);
-
- if (device->pdata) {
- free(device->pdata->blocks);
- free(device->pdata->addrs);
- free(device->pdata);
- }
-}
-
-static void local_shutdown(struct iio_context *ctx)
-{
- /* Free the backend data stored in every device structure */
- unsigned int i;
-
- for (i = 0; i < ctx->nb_devices; i++) {
- struct iio_device *dev = ctx->devices[i];
-
- iio_device_close(dev);
- local_free_pdata(dev);
- }
-
- free(ctx->pdata);
-}
-
-/** Shrinks the first nb characters of a string
- * e.g. strcut("foobar", 4) replaces the content with "ar". */
-static void strcut(char *str, int nb)
-{
- char *ptr = str + nb;
- while (*ptr)
- *str++ = *ptr++;
- *str = 0;
-}
-
-static int set_channel_name(struct iio_channel *chn)
-{
- struct iio_channel_pdata *pdata = chn->pdata;
- size_t prefix_len = 0;
- const char *attr0;
- const char *ptr;
- unsigned int i;
-
- if (chn->nb_attrs + pdata->nb_protected_attrs < 2)
- return 0;
-
- if (chn->nb_attrs)
- attr0 = ptr = chn->attrs[0].name;
- else
- attr0 = ptr = pdata->protected_attrs[0].name;
-
- while (true) {
- bool can_fix = true;
- size_t len;
-
- ptr = strchr(ptr, '_');
- if (!ptr)
- break;
-
- len = ptr - attr0 + 1;
- for (i = 1; can_fix && i < chn->nb_attrs; i++)
- can_fix = !strncmp(attr0, chn->attrs[i].name, len);
-
- for (i = !chn->nb_attrs;
- can_fix && i < pdata->nb_protected_attrs; i++) {
- can_fix = !strncmp(attr0,
- pdata->protected_attrs[i].name, len);
- }
-
- if (!can_fix)
- break;
-
- prefix_len = len;
- ptr = ptr + 1;
- }
-
- if (prefix_len) {
- char *name;
-
- name = malloc(prefix_len);
- if (!name)
- return -ENOMEM;
- strncpy(name, attr0, prefix_len - 1);
- name[prefix_len - 1] = '\0';
- DEBUG("Setting name of channel %s to %s\n", chn->id, name);
- chn->name = name;
-
- /* Shrink the attribute name */
- for (i = 0; i < chn->nb_attrs; i++)
- strcut(chn->attrs[i].name, prefix_len);
- for (i = 0; i < pdata->nb_protected_attrs; i++)
- strcut(pdata->protected_attrs[i].name, prefix_len);
- }
-
- return 0;
-}
-
-/*
- * Used to generate the timeout parameter for operations like poll. Returns the
- * number of ms until it is timeout_rel ms after the time specified in start. If
- * timeout_rel is 0 returns -1 to indicate no timeout.
- *
- * The timeout that is specified for IIO operations is the maximum time a buffer
- * push() or refill() operation should take before returning. poll() is used to
- * wait for either data activity or for the timeout to elapse. poll() might get
- * interrupted in which case it is called again or the read()/write() operation
- * might not complete the full buffer size in one call in which case we go back
- * to poll() again as well. Passing the same timeout as before would increase
- * the total timeout and if repeated interruptions occur (e.g. by a timer
- * signal) the operation might never time out or with significant delay. Hence
- * before each poll() invocation the timeout is recalculated relative to the
- * start of refill() or push() operation.
- */
-static int get_rel_timeout_ms(struct timespec *start, unsigned int timeout_rel)
-{
- struct timespec now;
- int diff_ms;
-
- if (timeout_rel == 0) /* No timeout */
- return -1;
-
- clock_gettime(CLOCK_MONOTONIC, &now);
-
- diff_ms = (now.tv_sec - start->tv_sec) * 1000;
- diff_ms += (now.tv_nsec - start->tv_nsec) / 1000000;
-
- if (diff_ms >= timeout_rel) /* Expired */
- return 0;
- if (diff_ms > 0) /* Should never be false, but lets be safe */
- timeout_rel -= diff_ms;
- if (timeout_rel > INT_MAX)
- return INT_MAX;
-
- return (int) timeout_rel;
-}
-
-static int device_check_ready(const struct iio_device *dev, short events,
- struct timespec *start)
-{
- struct pollfd pollfd[2] = {
- {
- .fd = dev->pdata->fd,
- .events = events,
- }, {
- .fd = dev->pdata->cancel_fd,
- .events = POLLIN,
- }
- };
- unsigned int rw_timeout_ms = dev->ctx->pdata->rw_timeout_ms;
- int timeout_rel;
- int ret;
-
- if (!dev->pdata->blocking)
- return 0;
-
- do {
- timeout_rel = get_rel_timeout_ms(start, rw_timeout_ms);
- ret = poll(pollfd, 2, timeout_rel);
- } while (ret == -1 && errno == EINTR);
-
- if ((pollfd[1].revents & POLLIN))
- return -EBADF;
-
- if (ret < 0)
- return -errno;
- if (!ret)
- return -ETIMEDOUT;
- if (pollfd[0].revents & POLLNVAL)
- return -EBADF;
- if (!(pollfd[0].revents & events))
- return -EIO;
- return 0;
-}
-
-static ssize_t local_read(const struct iio_device *dev,
- void *dst, size_t len, uint32_t *mask, size_t words)
-{
- struct iio_device_pdata *pdata = dev->pdata;
- uintptr_t ptr = (uintptr_t) dst;
- struct timespec start;
- ssize_t readsize;
- ssize_t ret;
-
- if (pdata->fd == -1)
- return -EBADF;
- if (words != dev->words)
- return -EINVAL;
-
- memcpy(mask, dev->mask, words);
-
- if (len == 0)
- return 0;
-
- clock_gettime(CLOCK_MONOTONIC, &start);
-
- while (len > 0) {
- ret = device_check_ready(dev, POLLIN, &start);
- if (ret < 0)
- break;
-
- do {
- ret = read(pdata->fd, (void *) ptr, len);
- } while (ret == -1 && errno == EINTR);
-
- if (ret == -1) {
- if (pdata->blocking && errno == EAGAIN)
- continue;
- ret = -errno;
- break;
- } else if (ret == 0) {
- ret = -EIO;
- break;
- }
-
- ptr += ret;
- len -= ret;
- }
-
- readsize = (ssize_t)(ptr - (uintptr_t) dst);
- if ((ret > 0 || ret == -EAGAIN) && (readsize > 0))
- return readsize;
- else
- return ret;
-}
-
-static ssize_t local_write(const struct iio_device *dev,
- const void *src, size_t len)
-{
- struct iio_device_pdata *pdata = dev->pdata;
- uintptr_t ptr = (uintptr_t) src;
- struct timespec start;
- ssize_t writtensize;
- ssize_t ret;
-
- if (pdata->fd == -1)
- return -EBADF;
-
- if (len == 0)
- return 0;
-
- clock_gettime(CLOCK_MONOTONIC, &start);
-
- while (len > 0) {
- ret = device_check_ready(dev, POLLOUT, &start);
- if (ret < 0)
- break;
-
- do {
- ret = write(pdata->fd, (void *) ptr, len);
- } while (ret == -1 && errno == EINTR);
-
- if (ret == -1) {
- if (pdata->blocking && errno == EAGAIN)
- continue;
-
- ret = -errno;
- break;
- } else if (ret == 0) {
- ret = -EIO;
- break;
- }
-
- ptr += ret;
- len -= ret;
- }
-
- writtensize = (ssize_t)(ptr - (uintptr_t) src);
- if ((ret > 0 || ret == -EAGAIN) && (writtensize > 0))
- return writtensize;
- else
- return ret;
-}
-
-static ssize_t local_enable_buffer(const struct iio_device *dev)
-{
- struct iio_device_pdata *pdata = dev->pdata;
- ssize_t ret = 0;
-
- if (!pdata->buffer_enabled) {
- ret = local_write_dev_attr(dev,
- "buffer/enable", "1", 2, false);
- if (ret >= 0)
- pdata->buffer_enabled = true;
- }
-
- return ret;
-}
-
-static int local_set_kernel_buffers_count(const struct iio_device *dev,
- unsigned int nb_blocks)
-{
- struct iio_device_pdata *pdata = dev->pdata;
-
- if (pdata->fd != -1)
- return -EBUSY;
-
- pdata->max_nb_blocks = nb_blocks;
-
- return 0;
-}
-
-static ssize_t local_get_buffer(const struct iio_device *dev,
- void **addr_ptr, size_t bytes_used,
- uint32_t *mask, size_t words)
-{
- struct block block;
- struct iio_device_pdata *pdata = dev->pdata;
- struct timespec start;
- char err_str[1024];
- int f = pdata->fd;
- ssize_t ret;
-
- if (!pdata->is_high_speed)
- return -ENOSYS;
- if (f == -1)
- return -EBADF;
- if (!addr_ptr)
- return -EINVAL;
-
- if (pdata->last_dequeued >= 0) {
- struct block *last_block = &pdata->blocks[pdata->last_dequeued];
-
- if (pdata->cyclic) {
- if (pdata->cyclic_buffer_enqueued)
- return -EBUSY;
- pdata->blocks[0].flags |= BLOCK_FLAG_CYCLIC;
- pdata->cyclic_buffer_enqueued = true;
- }
-
- last_block->bytes_used = bytes_used;
- ret = (ssize_t) ioctl_nointr(f,
- BLOCK_ENQUEUE_IOCTL, last_block);
- if (ret) {
- ret = (ssize_t) -errno;
- iio_strerror(errno, err_str, sizeof(err_str));
- ERROR("Unable to enqueue block: %s\n", err_str);
- return ret;
- }
-
- if (pdata->cyclic) {
- *addr_ptr = pdata->addrs[pdata->last_dequeued];
- return (ssize_t) last_block->bytes_used;
- }
-
- pdata->last_dequeued = -1;
- }
-
- clock_gettime(CLOCK_MONOTONIC, &start);
-
- do {
- ret = (ssize_t) device_check_ready(dev, POLLIN | POLLOUT, &start);
- if (ret < 0)
- return ret;
-
- memset(&block, 0, sizeof(block));
- ret = (ssize_t) ioctl_nointr(f, BLOCK_DEQUEUE_IOCTL, &block);
- } while (pdata->blocking && ret == -1 && errno == EAGAIN);
-
- if (ret) {
- ret = (ssize_t) -errno;
- if ((!pdata->blocking && ret != -EAGAIN) ||
- (pdata->blocking && ret != -ETIMEDOUT)) {
- iio_strerror(errno, err_str, sizeof(err_str));
- ERROR("Unable to dequeue block: %s\n", err_str);
- }
- return ret;
- }
-
- /* Requested buffer size is too big! */
- if (pdata->last_dequeued < 0 && bytes_used != block.size)
- return -EFBIG;
-
- pdata->last_dequeued = block.id;
- *addr_ptr = pdata->addrs[block.id];
- return (ssize_t) block.bytes_used;
-}
-
-static ssize_t local_read_all_dev_attrs(const struct iio_device *dev,
- char *dst, size_t len, enum iio_attr_type type)
-{
- unsigned int i, nb;
- char **attrs;
- char *ptr = dst;
-
- switch (type) {
- case IIO_ATTR_TYPE_DEVICE:
- nb = dev->nb_attrs;
- attrs = dev->attrs;
- break;
- case IIO_ATTR_TYPE_DEBUG:
- nb = dev->nb_debug_attrs;
- attrs = dev->debug_attrs;
- break;
- case IIO_ATTR_TYPE_BUFFER:
- nb = dev->nb_buffer_attrs;
- attrs = dev->buffer_attrs;
- break;
- default:
- return -EINVAL;
- break;
- }
-
- for (i = 0; len >= 4 && i < nb; i++) {
- /* Recursive! */
- ssize_t ret = local_read_dev_attr(dev, attrs[i],
- ptr + 4, len - 4, type);
- *(uint32_t *) ptr = iio_htobe32(ret);
-
- /* Align the length to 4 bytes */
- if (ret > 0 && ret & 3)
- ret = ((ret >> 2) + 1) << 2;
- ptr += 4 + (ret < 0 ? 0 : ret);
- len -= 4 + (ret < 0 ? 0 : ret);
- }
-
- return ptr - dst;
-}
-
-static ssize_t local_read_all_chn_attrs(const struct iio_channel *chn,
- char *dst, size_t len)
-{
- unsigned int i;
- char *ptr = dst;
-
- for (i = 0; len >= 4 && i < chn->nb_attrs; i++) {
- /* Recursive! */
- ssize_t ret = local_read_chn_attr(chn,
- chn->attrs[i].name, ptr + 4, len - 4);
- *(uint32_t *) ptr = iio_htobe32(ret);
-
- /* Align the length to 4 bytes */
- if (ret > 0 && ret & 3)
- ret = ((ret >> 2) + 1) << 2;
- ptr += 4 + (ret < 0 ? 0 : ret);
- len -= 4 + (ret < 0 ? 0 : ret);
- }
-
- return ptr - dst;
-}
-
-static int local_buffer_analyze(unsigned int nb, const char *src, size_t len)
-{
- while (nb--) {
- int32_t val;
-
- if (len < 4)
- return -EINVAL;
-
- val = (int32_t) iio_be32toh(*(uint32_t *) src);
- src += 4;
- len -= 4;
-
- if (val > 0) {
- if ((uint32_t) val > len)
- return -EINVAL;
-
- /* Align the length to 4 bytes */
- if (val & 3)
- val = ((val >> 2) + 1) << 2;
- len -= val;
- src += val;
- }
- }
-
- /* We should have analyzed the whole buffer by now */
- return !len ? 0 : -EINVAL;
-}
-
-static ssize_t local_write_all_dev_attrs(const struct iio_device *dev,
- const char *src, size_t len, enum iio_attr_type type)
-{
- unsigned int i, nb;
- char **attrs;
- const char *ptr = src;
-
- switch (type) {
- case IIO_ATTR_TYPE_DEVICE:
- nb = dev->nb_attrs;
- attrs = dev->attrs;
- break;
- case IIO_ATTR_TYPE_DEBUG:
- nb = dev->nb_debug_attrs;
- attrs = dev->debug_attrs;
- break;
- case IIO_ATTR_TYPE_BUFFER:
- nb = dev->nb_buffer_attrs;
- attrs = dev->buffer_attrs;
- break;
- default:
- return -EINVAL;
- break;
- }
-
- /* First step: Verify that the buffer is in the correct format */
- if (local_buffer_analyze(nb, src, len))
- return -EINVAL;
-
- /* Second step: write the attributes */
- for (i = 0; i < nb; i++) {
- int32_t val = (int32_t) iio_be32toh(*(uint32_t *) ptr);
- ptr += 4;
-
- if (val > 0) {
- local_write_dev_attr(dev, attrs[i], ptr, val, type);
-
- /* Align the length to 4 bytes */
- if (val & 3)
- val = ((val >> 2) + 1) << 2;
- ptr += val;
- }
- }
-
- return ptr - src;
-}
-
-static ssize_t local_write_all_chn_attrs(const struct iio_channel *chn,
- const char *src, size_t len)
-{
- unsigned int i, nb = chn->nb_attrs;
- const char *ptr = src;
-
- /* First step: Verify that the buffer is in the correct format */
- if (local_buffer_analyze(nb, src, len))
- return -EINVAL;
-
- /* Second step: write the attributes */
- for (i = 0; i < nb; i++) {
- int32_t val = (int32_t) iio_be32toh(*(uint32_t *) ptr);
- ptr += 4;
-
- if (val > 0) {
- local_write_chn_attr(chn, chn->attrs[i].name, ptr, val);
-
- /* Align the length to 4 bytes */
- if (val & 3)
- val = ((val >> 2) + 1) << 2;
- ptr += val;
- }
- }
-
- return ptr - src;
-}
-
-static ssize_t local_read_dev_attr(const struct iio_device *dev,
- const char *attr, char *dst, size_t len, enum iio_attr_type type)
-{
- FILE *f;
- char buf[1024];
- ssize_t ret;
-
- if (!attr)
- return local_read_all_dev_attrs(dev, dst, len, type);
-
- switch (type) {
- case IIO_ATTR_TYPE_DEVICE:
- iio_snprintf(buf, sizeof(buf), "/sys/bus/iio/devices/%s/%s",
- dev->id, attr);
- break;
- case IIO_ATTR_TYPE_DEBUG:
- iio_snprintf(buf, sizeof(buf), "/sys/kernel/debug/iio/%s/%s",
- dev->id, attr);
- break;
- case IIO_ATTR_TYPE_BUFFER:
- iio_snprintf(buf, sizeof(buf), "/sys/bus/iio/devices/%s/buffer/%s",
- dev->id, attr);
- break;
- default:
- return -EINVAL;
- }
-
- f = fopen(buf, "re");
- if (!f)
- return -errno;
-
- ret = fread(dst, 1, len, f);
- if (ret > 0)
- dst[ret - 1] = '\0';
- fflush(f);
- if (ferror(f))
- ret = -errno;
- fclose(f);
- return ret ? ret : -EIO;
-}
-
-static ssize_t local_write_dev_attr(const struct iio_device *dev,
- const char *attr, const char *src, size_t len, enum iio_attr_type type)
-{
- FILE *f;
- char buf[1024];
- ssize_t ret;
-
- if (!attr)
- return local_write_all_dev_attrs(dev, src, len, type);
-
- switch (type) {
- case IIO_ATTR_TYPE_DEVICE:
- iio_snprintf(buf, sizeof(buf), "/sys/bus/iio/devices/%s/%s",
- dev->id, attr);
- break;
- case IIO_ATTR_TYPE_DEBUG:
- iio_snprintf(buf, sizeof(buf), "/sys/kernel/debug/iio/%s/%s",
- dev->id, attr);
- break;
- case IIO_ATTR_TYPE_BUFFER:
- iio_snprintf(buf, sizeof(buf), "/sys/bus/iio/devices/%s/buffer/%s",
- dev->id, attr);
- break;
- default:
- return -EINVAL;
- }
-
- f = fopen(buf, "we");
- if (!f)
- return -errno;
-
- ret = fwrite(src, 1, len, f);
- fflush(f);
- if (ferror(f))
- ret = -errno;
- fclose(f);
- return ret ? ret : -EIO;
-}
-
-static const char * get_filename(const struct iio_channel *chn,
- const char *attr)
-{
- unsigned int i;
- for (i = 0; i < chn->nb_attrs; i++)
- if (!strcmp(attr, chn->attrs[i].name))
- return chn->attrs[i].filename;
- return attr;
-}
-
-static ssize_t local_read_chn_attr(const struct iio_channel *chn,
- const char *attr, char *dst, size_t len)
-{
- if (!attr)
- return local_read_all_chn_attrs(chn, dst, len);
-
- attr = get_filename(chn, attr);
- return local_read_dev_attr(chn->dev, attr, dst, len, false);
-}
-
-static ssize_t local_write_chn_attr(const struct iio_channel *chn,
- const char *attr, const char *src, size_t len)
-{
- if (!attr)
- return local_write_all_chn_attrs(chn, src, len);
-
- attr = get_filename(chn, attr);
- return local_write_dev_attr(chn->dev, attr, src, len, false);
-}
-
-static int channel_write_state(const struct iio_channel *chn, bool en)
-{
- ssize_t ret;
-
- if (!chn->pdata->enable_fn) {
- ERROR("Libiio bug: No \"en\" attribute parsed\n");
- return -EINVAL;
- }
-
- ret = local_write_chn_attr(chn, chn->pdata->enable_fn, en ? "1" : "0", 2);
- if (ret < 0)
- return (int) ret;
- else
- return 0;
-}
-
-static int enable_high_speed(const struct iio_device *dev)
-{
- struct block_alloc_req req;
- struct iio_device_pdata *pdata = dev->pdata;
- unsigned int nb_blocks;
- unsigned int i;
- int ret, fd = pdata->fd;
-
- /*
- * For the BLOCK_ALLOC_IOCTL ioctl it is not possible to distingush
- * between an error during the allocation (e.g. incorrect size) or
- * whether the high-speed interface is not supported. BLOCK_FREE_IOCTL does
- * never fail if the device supports the high-speed interface, so we use it
- * here. Calling it when no blocks are allocated the ioctl has no effect.
- */
- ret = ioctl_nointr(fd, BLOCK_FREE_IOCTL, NULL);
- if (ret < 0)
- return -ENOSYS;
-
- if (pdata->cyclic) {
- nb_blocks = 1;
- DEBUG("Enabling cyclic mode\n");
- } else {
- nb_blocks = pdata->max_nb_blocks;
- DEBUG("Cyclic mode not enabled\n");
- }
-
- pdata->blocks = calloc(nb_blocks, sizeof(*pdata->blocks));
- if (!pdata->blocks)
- return -ENOMEM;
-
- pdata->addrs = calloc(nb_blocks, sizeof(*pdata->addrs));
- if (!pdata->addrs) {
- free(pdata->blocks);
- pdata->blocks = NULL;
- return -ENOMEM;
- }
-
- req.id = 0;
- req.type = 0;
- req.size = pdata->samples_count *
- iio_device_get_sample_size_mask(dev, dev->mask, dev->words);
- req.count = nb_blocks;
-
- ret = ioctl_nointr(fd, BLOCK_ALLOC_IOCTL, &req);
- if (ret < 0) {
- ret = -errno;
- goto err_freemem;
- }
-
- if (req.count == 0) {
- ret = -ENOMEM;
- goto err_block_free;
- }
-
- /* We might get less blocks than what we asked for */
- pdata->allocated_nb_blocks = req.count;
-
- /* mmap all the blocks */
- for (i = 0; i < pdata->allocated_nb_blocks; i++) {
- pdata->blocks[i].id = i;
- ret = ioctl_nointr(fd, BLOCK_QUERY_IOCTL, &pdata->blocks[i]);
- if (ret) {
- ret = -errno;
- goto err_munmap;
- }
-
- ret = ioctl_nointr(fd, BLOCK_ENQUEUE_IOCTL, &pdata->blocks[i]);
- if (ret) {
- ret = -errno;
- goto err_munmap;
- }
-
- pdata->addrs[i] = mmap(0, pdata->blocks[i].size,
- PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, pdata->blocks[i].offset);
- if (pdata->addrs[i] == MAP_FAILED) {
- ret = -errno;
- goto err_munmap;
- }
- }
-
- pdata->last_dequeued = -1;
- return 0;
-
-err_munmap:
- for (; i > 0; i--)
- munmap(pdata->addrs[i - 1], pdata->blocks[i - 1].size);
-err_block_free:
- ioctl_nointr(fd, BLOCK_FREE_IOCTL, 0);
- pdata->allocated_nb_blocks = 0;
-err_freemem:
- free(pdata->addrs);
- pdata->addrs = NULL;
- free(pdata->blocks);
- pdata->blocks = NULL;
- return ret;
-}
-
-static int local_open(const struct iio_device *dev,
- size_t samples_count, bool cyclic)
-{
- unsigned int i;
- int ret;
- char buf[1024];
- struct iio_device_pdata *pdata = dev->pdata;
-
- if (pdata->fd != -1)
- return -EBUSY;
-
- ret = local_write_dev_attr(dev, "buffer/enable", "0", 2, false);
- if (ret < 0)
- return ret;
-
- iio_snprintf(buf, sizeof(buf), "%lu", (unsigned long) samples_count);
- ret = local_write_dev_attr(dev, "buffer/length",
- buf, strlen(buf) + 1, false);
- if (ret < 0)
- return ret;
-
- pdata->cancel_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
- if (pdata->cancel_fd == -1)
- return -errno;
-
- iio_snprintf(buf, sizeof(buf), "/dev/%s", dev->id);
- pdata->fd = open(buf, O_RDWR | O_CLOEXEC | O_NONBLOCK);
- if (pdata->fd == -1) {
- ret = -errno;
- goto err_close_cancel_fd;
- }
-
- /* Disable channels */
- for (i = 0; i < dev->nb_channels; i++) {
- struct iio_channel *chn = dev->channels[i];
- if (chn->index >= 0 && !iio_channel_is_enabled(chn)) {
- ret = channel_write_state(chn, false);
- if (ret < 0)
- goto err_close;
- }
- }
- /* Enable channels */
- for (i = 0; i < dev->nb_channels; i++) {
- struct iio_channel *chn = dev->channels[i];
- if (chn->index >= 0 && iio_channel_is_enabled(chn)) {
- ret = channel_write_state(chn, true);
- if (ret < 0)
- goto err_close;
- }
- }
-
- pdata->cyclic = cyclic;
- pdata->cyclic_buffer_enqueued = false;
- pdata->buffer_enabled = false;
- pdata->samples_count = samples_count;
-
- ret = enable_high_speed(dev);
- if (ret < 0 && ret != -ENOSYS)
- goto err_close;
-
- pdata->is_high_speed = !ret;
-
- if (!pdata->is_high_speed) {
- unsigned long size = samples_count * pdata->max_nb_blocks;
- WARNING("High-speed mode not enabled\n");
-
- /* Cyclic mode is only supported in high-speed mode */
- if (cyclic) {
- ret = -EPERM;
- goto err_close;
- }
-
- /* Increase the size of the kernel buffer, when using the
- * low-speed interface. This avoids losing samples when
- * refilling the iio_buffer. */
- iio_snprintf(buf, sizeof(buf), "%lu", size);
- ret = local_write_dev_attr(dev, "buffer/length",
- buf, strlen(buf) + 1, false);
- if (ret < 0)
- goto err_close;
- }
-
- ret = local_enable_buffer(dev);
- if (ret < 0)
- goto err_close;
-
- return 0;
-err_close:
- close(pdata->fd);
- pdata->fd = -1;
-err_close_cancel_fd:
- close(pdata->cancel_fd);
- pdata->cancel_fd = -1;
- return ret;
-}
-
-static int local_close(const struct iio_device *dev)
-{
- struct iio_device_pdata *pdata = dev->pdata;
- unsigned int i;
- int ret;
-
- if (pdata->fd == -1)
- return -EBADF;
-
- if (pdata->is_high_speed) {
- unsigned int i;
- for (i = 0; i < pdata->allocated_nb_blocks; i++)
- munmap(pdata->addrs[i], pdata->blocks[i].size);
- ioctl_nointr(pdata->fd, BLOCK_FREE_IOCTL, 0);
- pdata->allocated_nb_blocks = 0;
- free(pdata->addrs);
- pdata->addrs = NULL;
- free(pdata->blocks);
- pdata->blocks = NULL;
- }
-
- ret = close(pdata->fd);
- if (ret)
- return ret;
-
- close(pdata->cancel_fd);
-
- pdata->fd = -1;
- pdata->cancel_fd = -1;
-
- ret = local_write_dev_attr(dev, "buffer/enable", "0", 2, false);
-
- for (i = 0; i < dev->nb_channels; i++) {
- struct iio_channel *chn = dev->channels[i];
-
- if (chn->pdata->enable_fn)
- channel_write_state(chn, false);
- }
-
- return (ret < 0) ? ret : 0;
-}
-
-static int local_get_fd(const struct iio_device *dev)
-{
- if (dev->pdata->fd == -1)
- return -EBADF;
- else
- return dev->pdata->fd;
-}
-
-static int local_set_blocking_mode(const struct iio_device *dev, bool blocking)
-{
- if (dev->pdata->fd == -1)
- return -EBADF;
-
- if (dev->pdata->cyclic)
- return -EPERM;
-
- dev->pdata->blocking = blocking;
-
- return 0;
-}
-
-static int local_get_trigger(const struct iio_device *dev,
- const struct iio_device **trigger)
-{
- char buf[1024];
- unsigned int i;
- ssize_t nb = local_read_dev_attr(dev, "trigger/current_trigger",
- buf, sizeof(buf), false);
- if (nb < 0) {
- *trigger = NULL;
- return (int) nb;
- }
-
- if (buf[0] == '\0') {
- *trigger = NULL;
- return 0;
- }
-
- nb = dev->ctx->nb_devices;
- for (i = 0; i < (size_t) nb; i++) {
- const struct iio_device *cur = dev->ctx->devices[i];
- if (cur->name && !strcmp(cur->name, buf)) {
- *trigger = cur;
- return 0;
- }
- }
- return -ENXIO;
-}
-
-static int local_set_trigger(const struct iio_device *dev,
- const struct iio_device *trigger)
-{
- ssize_t nb;
- const char *value = trigger ? trigger->name : "";
- nb = local_write_dev_attr(dev, "trigger/current_trigger",
- value, strlen(value) + 1, false);
- if (nb < 0)
- return (int) nb;
- else
- return 0;
-}
-
-static bool is_channel(const char *attr, bool strict)
-{
- char *ptr = NULL;
- if (!strncmp(attr, "in_timestamp_", sizeof("in_timestamp_") - 1))
- return true;
- if (!strncmp(attr, "in_", 3))
- ptr = strchr(attr + 3, '_');
- else if (!strncmp(attr, "out_", 4))
- ptr = strchr(attr + 4, '_');
- if (!ptr)
- return false;
- if (!strict)
- return true;
- if (*(ptr - 1) >= '0' && *(ptr - 1) <= '9')
- return true;
-
- if (find_channel_modifier(ptr + 1, NULL) != IIO_NO_MOD)
- return true;
- return false;
-}
-
-static char * get_channel_id(const char *attr)
-{
- char *res, *ptr;
- size_t len;
-
- attr = strchr(attr, '_') + 1;
- ptr = strchr(attr, '_');
- if (find_channel_modifier(ptr + 1, &len) != IIO_NO_MOD)
- ptr += len + 1;
-
- res = malloc(ptr - attr + 1);
- if (!res)
- return NULL;
-
- memcpy(res, attr, ptr - attr);
- res[ptr - attr] = 0;
- return res;
-}
-
-static char * get_short_attr_name(struct iio_channel *chn, const char *attr)
-{
- char *ptr = strchr(attr, '_') + 1;
- size_t len;
-
- ptr = strchr(ptr, '_') + 1;
- if (find_channel_modifier(ptr, &len) != IIO_NO_MOD)
- ptr += len + 1;
-
- if (chn->name) {
- size_t len = strlen(chn->name);
- if (strncmp(chn->name, ptr, len) == 0 && ptr[len] == '_')
- ptr += len + 1;
- }
-
- return iio_strdup(ptr);
-}
-
-static int read_device_name(struct iio_device *dev)
-{
- char buf[1024];
- ssize_t ret = iio_device_attr_read(dev, "name", buf, sizeof(buf));
- if (ret < 0)
- return ret;
- else if (ret == 0)
- return -EIO;
-
- dev->name = iio_strdup(buf);
- if (!dev->name)
- return -ENOMEM;
- else
- return 0;
-}
-
-static int add_attr_to_device(struct iio_device *dev, const char *attr)
-{
- char **attrs, *name;
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(device_attrs_blacklist); i++)
- if (!strcmp(device_attrs_blacklist[i], attr))
- return 0;
-
- if (!strcmp(attr, "name"))
- return read_device_name(dev);
-
- name = iio_strdup(attr);
- if (!name)
- return -ENOMEM;
-
- attrs = realloc(dev->attrs, (1 + dev->nb_attrs) * sizeof(char *));
- if (!attrs) {
- free(name);
- return -ENOMEM;
- }
-
- attrs[dev->nb_attrs++] = name;
- dev->attrs = attrs;
- DEBUG("Added attr \'%s\' to device \'%s\'\n", attr, dev->id);
- return 0;
-}
-
-static int handle_protected_scan_element_attr(struct iio_channel *chn,
- const char *name, const char *path)
-{
- struct iio_device *dev = chn->dev;
- char buf[1024];
- int ret;
-
- if (!strcmp(name, "index")) {
- ret = local_read_dev_attr(dev, path, buf, sizeof(buf), false);
- if (ret > 0)
- chn->index = atol(buf);
-
- } else if (!strcmp(name, "type")) {
- ret = local_read_dev_attr(dev, path, buf, sizeof(buf), false);
- if (ret > 0) {
- char endian, sign;
-
- if (strchr(buf, 'X')) {
- sscanf(buf, "%ce:%c%u/%uX%u>>%u", &endian, &sign,
- &chn->format.bits, &chn->format.length,
- &chn->format.repeat, &chn->format.shift);
- } else {
- chn->format.repeat = 1;
- sscanf(buf, "%ce:%c%u/%u>>%u", &endian, &sign,
- &chn->format.bits, &chn->format.length,
- &chn->format.shift);
- }
- chn->format.is_signed = (sign == 's' || sign == 'S');
- chn->format.is_fully_defined =
- (sign == 'S' || sign == 'U'||
- chn->format.bits == chn->format.length);
- chn->format.is_be = endian == 'b';
- }
-
- } else if (!strcmp(name, "en")) {
- if (chn->pdata->enable_fn) {
- ERROR("Libiio bug: \"en\" attribute already parsed for channel %s!\n",
- chn->id);
- return -EINVAL;
- }
-
- chn->pdata->enable_fn = iio_strdup(path);
- if (!chn->pdata->enable_fn)
- return -ENOMEM;
-
- } else {
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int handle_scan_elements(struct iio_channel *chn)
-{
- struct iio_channel_pdata *pdata = chn->pdata;
- unsigned int i;
-
- for (i = 0; i < pdata->nb_protected_attrs; i++) {
- int ret = handle_protected_scan_element_attr(chn,
- pdata->protected_attrs[i].name,
- pdata->protected_attrs[i].filename);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
-static int add_protected_attr(struct iio_channel *chn, char *name, char *fn)
-{
- struct iio_channel_pdata *pdata = chn->pdata;
- struct iio_channel_attr *attrs;
-
- attrs = realloc(pdata->protected_attrs,
- (1 + pdata->nb_protected_attrs) * sizeof(*attrs));
- if (!attrs)
- return -ENOMEM;
-
- attrs[pdata->nb_protected_attrs].name = name;
- attrs[pdata->nb_protected_attrs++].filename = fn;
- pdata->protected_attrs = attrs;
-
- DEBUG("Add protected attr \'%s\' to channel \'%s\'\n", name, chn->id);
- return 0;
-}
-
-static void free_protected_attrs(struct iio_channel *chn)
-{
- struct iio_channel_pdata *pdata = chn->pdata;
- unsigned int i;
-
- for (i = 0; i < pdata->nb_protected_attrs; i++) {
- free(pdata->protected_attrs[i].name);
- free(pdata->protected_attrs[i].filename);
- }
-
- free(pdata->protected_attrs);
- pdata->nb_protected_attrs = 0;
- pdata->protected_attrs = NULL;
-}
-
-static int add_attr_to_channel(struct iio_channel *chn,
- const char *attr, const char *path, bool is_scan_element)
-{
- struct iio_channel_attr *attrs;
- char *fn, *name = get_short_attr_name(chn, attr);
- if (!name)
- return -ENOMEM;
-
- fn = iio_strdup(path);
- if (!fn)
- goto err_free_name;
-
- if (is_scan_element) {
- int ret = add_protected_attr(chn, name, fn);
-
- if (ret < 0)
- goto err_free_fn;
-
- return 0;
- }
-
- attrs = realloc(chn->attrs, (1 + chn->nb_attrs) *
- sizeof(struct iio_channel_attr));
- if (!attrs)
- goto err_free_fn;
-
- attrs[chn->nb_attrs].filename = fn;
- attrs[chn->nb_attrs++].name = name;
- chn->attrs = attrs;
- DEBUG("Added attr \'%s\' to channel \'%s\'\n", name, chn->id);
- return 0;
-
-err_free_fn:
- free(fn);
-err_free_name:
- free(name);
- return -ENOMEM;
-}
-
-static int add_channel_to_device(struct iio_device *dev,
- struct iio_channel *chn)
-{
- struct iio_channel **channels = realloc(dev->channels,
- (dev->nb_channels + 1) * sizeof(struct iio_channel *));
- if (!channels)
- return -ENOMEM;
-
- channels[dev->nb_channels++] = chn;
- dev->channels = channels;
- DEBUG("Added %s channel \'%s\' to device \'%s\'\n",
- chn->is_output ? "output" : "input", chn->id, dev->id);
-
- return 0;
-}
-
-static int add_device_to_context(struct iio_context *ctx,
- struct iio_device *dev)
-{
- struct iio_device **devices = realloc(ctx->devices,
- (ctx->nb_devices + 1) * sizeof(struct iio_device *));
- if (!devices)
- return -ENOMEM;
-
- devices[ctx->nb_devices++] = dev;
- ctx->devices = devices;
- DEBUG("Added device \'%s\' to context \'%s\'\n", dev->id, ctx->name);
- return 0;
-}
-
-static struct iio_channel *create_channel(struct iio_device *dev,
- char *id, const char *attr, const char *path,
- bool is_scan_element)
-{
- struct iio_channel *chn = zalloc(sizeof(*chn));
- if (!chn)
- return NULL;
-
- chn->pdata = zalloc(sizeof(*chn->pdata));
- if (!chn->pdata)
- goto err_free_chn;
-
- if (!strncmp(attr, "out_", 4))
- chn->is_output = true;
- else if (strncmp(attr, "in_", 3))
- goto err_free_chn_pdata;
-
- chn->dev = dev;
- chn->id = id;
- chn->is_scan_element = is_scan_element;
- chn->index = -ENOENT;
-
- if (!add_attr_to_channel(chn, attr, path, is_scan_element))
- return chn;
-
-err_free_chn_pdata:
- free(chn->pdata->enable_fn);
- free(chn->pdata);
-err_free_chn:
- free(chn);
- return NULL;
-}
-
-static int add_channel(struct iio_device *dev, const char *name,
- const char *path, bool dir_is_scan_elements)
-{
- struct iio_channel *chn;
- char *channel_id;
- unsigned int i;
- int ret;
-
- channel_id = get_channel_id(name);
- if (!channel_id)
- return -ENOMEM;
-
- for (i = 0; i < dev->nb_channels; i++) {
- chn = dev->channels[i];
- if (!strcmp(chn->id, channel_id)
- && chn->is_output == (name[0] == 'o')) {
- free(channel_id);
- ret = add_attr_to_channel(chn, name, path,
- dir_is_scan_elements);
- chn->is_scan_element = dir_is_scan_elements && !ret;
- return ret;
- }
- }
-
- chn = create_channel(dev, channel_id, name, path, dir_is_scan_elements);
- if (!chn) {
- free(channel_id);
- return -ENXIO;
- }
-
- iio_channel_init_finalize(chn);
-
- ret = add_channel_to_device(dev, chn);
- if (ret) {
- free(chn->pdata->enable_fn);
- free(chn->pdata);
- free_channel(chn);
- }
- return ret;
-}
-
-/*
- * Possible return values:
- * 0 = Attribute should not be moved to the channel
- * 1 = Attribute should be moved to the channel and it is a shared attribute
- * 2 = Attribute should be moved to the channel and it is a private attribute
- */
-static unsigned int is_global_attr(struct iio_channel *chn, const char *attr)
-{
- unsigned int len;
- char *ptr;
-
- if (!chn->is_output && !strncmp(attr, "in_", 3))
- attr += 3;
- else if (chn->is_output && !strncmp(attr, "out_", 4))
- attr += 4;
- else
- return 0;
-
- ptr = strchr(attr, '_');
- if (!ptr)
- return 0;
-
- len = ptr - attr;
-
- if (strncmp(chn->id, attr, len))
- return 0;
-
- DEBUG("Found match: %s and %s\n", chn->id, attr);
- if (chn->id[len] >= '0' && chn->id[len] <= '9') {
- if (chn->name) {
- size_t name_len = strlen(chn->name);
- if (strncmp(chn->name, attr + len + 1, name_len) == 0 &&
- attr[len + 1 + name_len] == '_')
- return 2;
- }
- return 1;
- } else if (chn->id[len] != '_') {
- return 0;
- }
-
- if (find_channel_modifier(chn->id + len + 1, NULL) != IIO_NO_MOD)
- return 1;
-
- return 0;
-}
-
-static int detect_global_attr(struct iio_device *dev, const char *attr,
- unsigned int level, bool *match)
-{
- unsigned int i;
-
- *match = false;
- for (i = 0; i < dev->nb_channels; i++) {
- struct iio_channel *chn = dev->channels[i];
- if (is_global_attr(chn, attr) == level) {
- int ret;
- *match = true;
- ret = add_attr_to_channel(chn, attr, attr, false);
- if (ret)
- return ret;
- }
- }
-
- return 0;
-}
-
-static int detect_and_move_global_attrs(struct iio_device *dev)
-{
- unsigned int i;
- char **ptr = dev->attrs;
-
- for (i = 0; i < dev->nb_attrs; i++) {
- const char *attr = dev->attrs[i];
- bool match;
- int ret;
-
- ret = detect_global_attr(dev, attr, 2, &match);
- if (ret)
- return ret;
-
- if (!match) {
- ret = detect_global_attr(dev, attr, 1, &match);
- if (ret)
- return ret;
- }
-
- if (match) {
- free(dev->attrs[i]);
- dev->attrs[i] = NULL;
- }
- }
-
- /* Find channels without an index */
- for (i = 0; i < dev->nb_attrs; i++) {
- const char *attr = dev->attrs[i];
- int ret;
-
- if (!dev->attrs[i])
- continue;
-
- if (is_channel(attr, false)) {
- ret = add_channel(dev, attr, attr, false);
- if (ret)
- return ret;
-
- free(dev->attrs[i]);
- dev->attrs[i] = NULL;
- }
- }
-
- for (i = 0; i < dev->nb_attrs; i++) {
- if (dev->attrs[i])
- *ptr++ = dev->attrs[i];
- }
-
- dev->nb_attrs = ptr - dev->attrs;
- if (!dev->nb_attrs) {
- free(dev->attrs);
- dev->attrs = NULL;
- }
-
- return 0;
-}
-
-static int add_buffer_attr(void *d, const char *path)
-{
- struct iio_device *dev = (struct iio_device *) d;
- const char *name = strrchr(path, '/') + 1;
- char **attrs, *attr;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(buffer_attrs_reserved); i++)
- if (!strcmp(buffer_attrs_reserved[i], name))
- return 0;
-
- attr = iio_strdup(name);
- if (!attr)
- return -ENOMEM;
-
- attrs = realloc(dev->buffer_attrs, (1 + dev->nb_buffer_attrs) * sizeof(char *));
- if (!attrs) {
- free(attr);
- return -ENOMEM;
- }
-
- attrs[dev->nb_buffer_attrs++] = attr;
- dev->buffer_attrs = attrs;
- DEBUG("Added buffer attr \'%s\' to device \'%s\'\n", attr, dev->id);
- return 0;
-}
-
-static int add_attr_or_channel_helper(struct iio_device *dev,
- const char *path, bool dir_is_scan_elements)
-{
- char buf[1024];
- const char *name = strrchr(path, '/') + 1;
-
- if (dir_is_scan_elements) {
- iio_snprintf(buf, sizeof(buf), "scan_elements/%s", name);
- path = buf;
- } else {
- if (!is_channel(name, true))
- return add_attr_to_device(dev, name);
- path = name;
- }
-
- return add_channel(dev, name, path, dir_is_scan_elements);
-}
-
-static int add_attr_or_channel(void *d, const char *path)
-{
- return add_attr_or_channel_helper((struct iio_device *) d, path, false);
-}
-
-static int add_scan_element(void *d, const char *path)
-{
- return add_attr_or_channel_helper((struct iio_device *) d, path, true);
-}
-
-static int foreach_in_dir(void *d, const char *path, bool is_dir,
- int (*callback)(void *, const char *))
-{
- struct dirent *entry;
- DIR *dir;
- int ret = 0;
-
- dir = opendir(path);
- if (!dir)
- return -errno;
-
- while (true) {
- struct stat st;
- char buf[1024];
-
- errno = 0;
- entry = readdir(dir);
- if (!entry) {
- if (!errno)
- break;
-
- ret = -errno;
- iio_strerror(errno, buf, sizeof(buf));
- ERROR("Unable to open directory %s: %s\n", path, buf);
- goto out_close_dir;
- }
-
- iio_snprintf(buf, sizeof(buf), "%s/%s", path, entry->d_name);
- if (stat(buf, &st) < 0) {
- ret = -errno;
- iio_strerror(errno, buf, sizeof(buf));
- ERROR("Unable to stat file: %s\n", buf);
- goto out_close_dir;
- }
-
- if (is_dir && S_ISDIR(st.st_mode) && entry->d_name[0] != '.')
- ret = callback(d, buf);
- else if (!is_dir && S_ISREG(st.st_mode))
- ret = callback(d, buf);
- else
- continue;
-
- if (ret < 0)
- goto out_close_dir;
- }
-
-out_close_dir:
- closedir(dir);
- return ret;
-}
-
-static int add_scan_elements(struct iio_device *dev, const char *devpath)
-{
- struct stat st;
- char buf[1024];
-
- iio_snprintf(buf, sizeof(buf), "%s/scan_elements", devpath);
-
- if (!stat(buf, &st) && S_ISDIR(st.st_mode)) {
- int ret = foreach_in_dir(dev, buf, false, add_scan_element);
- if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
-static int add_buffer_attributes(struct iio_device *dev, const char *devpath)
-{
- struct stat st;
- char buf[1024];
-
- iio_snprintf(buf, sizeof(buf), "%s/buffer", devpath);
-
- if (!stat(buf, &st) && S_ISDIR(st.st_mode)) {
- int ret = foreach_in_dir(dev, buf, false, add_buffer_attr);
- if (ret < 0)
- return ret;
-
- qsort(dev->buffer_attrs, dev->nb_buffer_attrs, sizeof(char *),
- iio_buffer_attr_compare);
- }
-
- return 0;
-}
-
-static int create_device(void *d, const char *path)
-{
- uint32_t *mask = NULL;
- unsigned int i;
- int ret;
- struct iio_context *ctx = d;
- struct iio_device *dev = zalloc(sizeof(*dev));
- if (!dev)
- return -ENOMEM;
-
- dev->pdata = zalloc(sizeof(*dev->pdata));
- if (!dev->pdata) {
- free(dev);
- return -ENOMEM;
- }
-
- dev->pdata->fd = -1;
- dev->pdata->blocking = true;
- dev->pdata->max_nb_blocks = NB_BLOCKS;
-
- dev->ctx = ctx;
- dev->id = iio_strdup(strrchr(path, '/') + 1);
- if (!dev->id) {
- local_free_pdata(dev);
- free(dev);
- return -ENOMEM;
- }
-
- ret = foreach_in_dir(dev, path, false, add_attr_or_channel);
- if (ret < 0)
- goto err_free_device;
-
- ret = add_buffer_attributes(dev, path);
- if (ret < 0)
- goto err_free_device;
-
- ret = add_scan_elements(dev, path);
- if (ret < 0)
- goto err_free_scan_elements;
-
- for (i = 0; i < dev->nb_channels; i++) {
- struct iio_channel *chn = dev->channels[i];
-
- set_channel_name(chn);
- ret = handle_scan_elements(chn);
- free_protected_attrs(chn);
- if (ret < 0)
- goto err_free_scan_elements;
- }
-
- ret = detect_and_move_global_attrs(dev);
- if (ret < 0)
- goto err_free_device;
-
- /* sorting is done after global attrs are added */
- for (i = 0; i < dev->nb_channels; i++) {
- struct iio_channel *chn = dev->channels[i];
- qsort(chn->attrs, chn->nb_attrs, sizeof(struct iio_channel_attr),
- iio_channel_attr_compare);
- }
- qsort(dev->attrs, dev->nb_attrs, sizeof(char *),
- iio_device_attr_compare);
-
- dev->words = (dev->nb_channels + 31) / 32;
- if (dev->words) {
- mask = calloc(dev->words, sizeof(*mask));
- if (!mask) {
- ret = -ENOMEM;
- goto err_free_device;
- }
- }
-
- dev->mask = mask;
-
- ret = add_device_to_context(ctx, dev);
- if (!ret)
- return 0;
-
-err_free_scan_elements:
- for (i = 0; i < dev->nb_channels; i++)
- free_protected_attrs(dev->channels[i]);
-err_free_device:
- local_free_pdata(dev);
- free_device(dev);
- return ret;
-}
-
-static int add_debug_attr(void *d, const char *path)
-{
- struct iio_device *dev = d;
- const char *attr = strrchr(path, '/') + 1;
- char **attrs, *name = iio_strdup(attr);
- if (!name)
- return -ENOMEM;
-
- attrs = realloc(dev->debug_attrs,
- (1 + dev->nb_debug_attrs) * sizeof(char *));
- if (!attrs) {
- free(name);
- return -ENOMEM;
- }
-
- attrs[dev->nb_debug_attrs++] = name;
- dev->debug_attrs = attrs;
- DEBUG("Added debug attr \'%s\' to device \'%s\'\n", name, dev->id);
- return 0;
-}
-
-static int add_debug(void *d, const char *path)
-{
- struct iio_context *ctx = d;
- const char *name = strrchr(path, '/') + 1;
- struct iio_device *dev = iio_context_find_device(ctx, name);
- if (!dev)
- return -ENODEV;
- else
- return foreach_in_dir(dev, path, false, add_debug_attr);
-}
-
-static int local_set_timeout(struct iio_context *ctx, unsigned int timeout)
-{
- ctx->pdata->rw_timeout_ms = timeout;
- return 0;
-}
-
-static void local_cancel(const struct iio_device *dev)
-{
- struct iio_device_pdata *pdata = dev->pdata;
- uint64_t event = 1;
- int ret;
-
- ret = write(pdata->cancel_fd, &event, sizeof(event));
- if (ret == -1) {
- /* If this happens something went very seriously wrong */
- char err_str[1024];
- iio_strerror(errno, err_str, sizeof(err_str));
- ERROR("Unable to signal cancellation event: %s\n", err_str);
- }
-}
-
-static struct iio_context * local_clone(
- const struct iio_context *ctx __attribute__((unused)))
-{
- return local_create_context();
-}
-
-static const struct iio_backend_ops local_ops = {
- .clone = local_clone,
- .open = local_open,
- .close = local_close,
- .get_fd = local_get_fd,
- .set_blocking_mode = local_set_blocking_mode,
- .read = local_read,
- .write = local_write,
- .set_kernel_buffers_count = local_set_kernel_buffers_count,
- .get_buffer = local_get_buffer,
- .read_device_attr = local_read_dev_attr,
- .write_device_attr = local_write_dev_attr,
- .read_channel_attr = local_read_chn_attr,
- .write_channel_attr = local_write_chn_attr,
- .get_trigger = local_get_trigger,
- .set_trigger = local_set_trigger,
- .shutdown = local_shutdown,
- .set_timeout = local_set_timeout,
- .cancel = local_cancel,
-};
-
-static void init_data_scale(struct iio_channel *chn)
-{
- char buf[1024];
- ssize_t ret;
-
- ret = iio_channel_attr_read(chn, "scale", buf, sizeof(buf));
- if (ret < 0) {
- chn->format.with_scale = false;
- } else {
- chn->format.with_scale = true;
- chn->format.scale = atof(buf);
- }
-}
-
-static void init_scan_elements(struct iio_context *ctx)
-{
- unsigned int i, j;
-
- for (i = 0; i < ctx->nb_devices; i++) {
- struct iio_device *dev = ctx->devices[i];
-
- for (j = 0; j < dev->nb_channels; j++)
- init_data_scale(dev->channels[j]);
- }
-}
-
-#ifdef WITH_LOCAL_CONFIG
-static int populate_context_attrs(struct iio_context *ctx, const char *file)
-{
- struct INI *ini;
- int ret;
-
- ini = ini_open(file);
- if (!ini) {
- /* INI file not present -> not an error */
- if (errno == ENOENT)
- return 0;
- else
- return -errno;
- }
-
- while (true) {
- const char *section;
- size_t len;
-
- ret = ini_next_section(ini, &section, &len);
- if (ret <= 0)
- goto out_close_ini;
-
- if (!strncmp(section, "Context Attributes", len))
- break;
- }
-
- do {
- const char *key, *value;
- char *new_key, *new_val;
- size_t klen, vlen;
-
- ret = ini_read_pair(ini, &key, &klen, &value, &vlen);
- if (ret <= 0)
- break;
-
- /* Create a dup of the strings read from the INI, since they are
- * not NULL-terminated. */
- new_key = strndup(key, klen);
- new_val = strndup(value, vlen);
-
- if (!new_key || !new_val)
- ret = -ENOMEM;
- else
- ret = iio_context_add_attr(ctx, new_key, new_val);
-
- free(new_key);
- free(new_val);
- } while (!ret);
-
-out_close_ini:
- ini_close(ini);
- return ret;
-}
-#endif
-
-struct iio_context * local_create_context(void)
-{
- int ret = -ENOMEM;
- unsigned int len;
- struct utsname uts;
- struct iio_context *ctx = zalloc(sizeof(*ctx));
- if (!ctx)
- goto err_set_errno;
-
- ctx->ops = &local_ops;
- ctx->name = "local";
-
- ctx->pdata = zalloc(sizeof(*ctx->pdata));
- if (!ctx->pdata) {
- free(ctx);
- goto err_set_errno;
- }
-
- local_set_timeout(ctx, DEFAULT_TIMEOUT_MS);
-
- uname(&uts);
- len = strlen(uts.sysname) + strlen(uts.nodename) + strlen(uts.release)
- + strlen(uts.version) + strlen(uts.machine);
- ctx->description = malloc(len + 5); /* 4 spaces + EOF */
- if (!ctx->description) {
- free(ctx->pdata);
- free(ctx);
- goto err_set_errno;
- }
-
- iio_snprintf(ctx->description, len + 5, "%s %s %s %s %s", uts.sysname,
- uts.nodename, uts.release, uts.version, uts.machine);
-
- ret = foreach_in_dir(ctx, "/sys/bus/iio/devices", true, create_device);
- if (ret < 0)
- goto err_context_destroy;
-
- qsort(ctx->devices, ctx->nb_devices, sizeof(struct iio_device *),
- iio_device_compare);
-
- foreach_in_dir(ctx, "/sys/kernel/debug/iio", true, add_debug);
-
- init_scan_elements(ctx);
-
-#ifdef WITH_LOCAL_CONFIG
- ret = populate_context_attrs(ctx, "/etc/libiio.ini");
- if (ret < 0)
- goto err_context_destroy;
-#endif
-
- ret = iio_context_add_attr(ctx, "local,kernel", uts.release);
- if (ret < 0)
- goto err_context_destroy;
-
- ret = iio_context_init(ctx);
- if (ret < 0)
- goto err_context_destroy;
-
- return ctx;
-
-err_context_destroy:
- iio_context_destroy(ctx);
-err_set_errno:
- errno = -ret;
- return NULL;
-}
-
-static int check_device(void *d, const char *path)
-{
- *(bool *)d = true;
- return 0;
-}
-
-int local_context_scan(struct iio_scan_result *scan_result)
-{
- struct iio_context_info **info;
- bool exists = false;
- char *desc, *uri;
- int ret;
-
- ret = foreach_in_dir(&exists, "/sys/bus/iio", true, check_device);
- if (ret < 0 || !exists)
- return 0;
-
- desc = iio_strdup("Local devices");
- if (!desc)
- return -ENOMEM;
-
- uri = iio_strdup("local:");
- if (!uri)
- goto err_free_desc;
-
- info = iio_scan_result_add(scan_result, 1);
- if (!info)
- goto err_free_uri;
-
- info[0]->description = desc;
- info[0]->uri = uri;
- return 0;
-
-err_free_uri:
- free(uri);
-err_free_desc:
- free(desc);
- return -ENOMEM;
-}