blob: d05afb6f31fee613f79e8f414abe34f5153c7f66 [file] [log] [blame]
/*
* 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;
}