diff options
Diffstat (limited to 'src/device.c')
-rw-r--r-- | src/device.c | 1151 |
1 files changed, 1151 insertions, 0 deletions
diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000..d3ec781 --- /dev/null +++ b/src/device.c @@ -0,0 +1,1151 @@ +/* + * 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 <inttypes.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +static char *get_attr_xml(const char *attr, size_t *length, enum iio_attr_type type) +{ + size_t len = sizeof("<attribute name=\"\" />") + strlen(attr); + char *str; + + switch(type){ + case IIO_ATTR_TYPE_DEVICE: + break; + case IIO_ATTR_TYPE_DEBUG: + len += (sizeof("debug-") - 1); + break; + case IIO_ATTR_TYPE_BUFFER: + len += (sizeof("buffer-") - 1); + break; + default: + return NULL; + } + + str = malloc(len); + if (!str) + return NULL; + + *length = len - 1; /* Skip the \0 */ + switch (type) { + case IIO_ATTR_TYPE_DEVICE: + iio_snprintf(str, len, "<attribute name=\"%s\" />", attr); + break; + case IIO_ATTR_TYPE_DEBUG: + iio_snprintf(str, len, "<debug-attribute name=\"%s\" />", attr); + break; + case IIO_ATTR_TYPE_BUFFER: + iio_snprintf(str, len, "<buffer-attribute name=\"%s\" />", attr); + break; + } + + return str; +} + +/* Returns a string containing the XML representation of this device */ +char * iio_device_get_xml(const struct iio_device *dev, size_t *length) +{ + size_t len = sizeof("<device id=\"\" name=\"\" ></device>") + + strlen(dev->id) + (dev->name ? strlen(dev->name) : 0); + char *ptr, *str, **attrs, **channels, **buffer_attrs, **debug_attrs; + size_t *attrs_len, *channels_len, *buffer_attrs_len, *debug_attrs_len; + unsigned int i, j, k; + + attrs_len = malloc(dev->nb_attrs * sizeof(*attrs_len)); + if (!attrs_len) + return NULL; + + attrs = malloc(dev->nb_attrs * sizeof(*attrs)); + if (!attrs) + goto err_free_attrs_len; + + for (i = 0; i < dev->nb_attrs; i++) { + char *xml = get_attr_xml(dev->attrs[i], &attrs_len[i], IIO_ATTR_TYPE_DEVICE); + if (!xml) + goto err_free_attrs; + attrs[i] = xml; + len += attrs_len[i]; + } + + channels_len = malloc(dev->nb_channels * sizeof(*channels_len)); + if (!channels_len) + goto err_free_attrs; + + channels = malloc(dev->nb_channels * sizeof(*channels)); + if (!channels) + goto err_free_channels_len; + + for (j = 0; j < dev->nb_channels; j++) { + char *xml = iio_channel_get_xml(dev->channels[j], + &channels_len[j]); + if (!xml) + goto err_free_channels; + channels[j] = xml; + len += channels_len[j]; + } + + buffer_attrs_len = malloc(dev->nb_buffer_attrs * + sizeof(*buffer_attrs_len)); + if (!buffer_attrs_len) + goto err_free_channels; + + buffer_attrs = malloc(dev->nb_buffer_attrs * sizeof(*buffer_attrs)); + if (!buffer_attrs) + goto err_free_buffer_attrs_len; + + for (k = 0; k < dev->nb_buffer_attrs; k++) { + char *xml = get_attr_xml(dev->buffer_attrs[k], + &buffer_attrs_len[k], IIO_ATTR_TYPE_BUFFER); + if (!xml) + goto err_free_buffer_attrs; + buffer_attrs[k] = xml; + len += buffer_attrs_len[k]; + } + + debug_attrs_len = malloc(dev->nb_debug_attrs * + sizeof(*debug_attrs_len)); + if (!debug_attrs_len) + goto err_free_buffer_attrs; + + debug_attrs = malloc(dev->nb_debug_attrs * sizeof(*debug_attrs)); + if (!debug_attrs) + goto err_free_debug_attrs_len; + + for (k = 0; k < dev->nb_debug_attrs; k++) { + char *xml = get_attr_xml(dev->debug_attrs[k], + &debug_attrs_len[k], IIO_ATTR_TYPE_DEBUG); + if (!xml) + goto err_free_debug_attrs; + debug_attrs[k] = xml; + len += debug_attrs_len[k]; + } + + str = malloc(len); + if (!str) + goto err_free_debug_attrs; + + iio_snprintf(str, len, "<device id=\"%s\"", dev->id); + ptr = strrchr(str, '\0'); + + if (dev->name) { + sprintf(ptr, " name=\"%s\"", dev->name); + ptr = strrchr(ptr, '\0'); + } + + strcpy(ptr, " >"); + ptr += 2; + + for (i = 0; i < dev->nb_channels; i++) { + strcpy(ptr, channels[i]); + ptr += channels_len[i]; + free(channels[i]); + } + + free(channels); + free(channels_len); + + for (i = 0; i < dev->nb_attrs; i++) { + strcpy(ptr, attrs[i]); + ptr += attrs_len[i]; + free(attrs[i]); + } + + free(attrs); + free(attrs_len); + + for (i = 0; i < dev->nb_buffer_attrs; i++) { + strcpy(ptr, buffer_attrs[i]); + ptr += buffer_attrs_len[i]; + free(buffer_attrs[i]); + } + + free(buffer_attrs); + free(buffer_attrs_len); + + for (i = 0; i < dev->nb_debug_attrs; i++) { + strcpy(ptr, debug_attrs[i]); + ptr += debug_attrs_len[i]; + free(debug_attrs[i]); + } + + free(debug_attrs); + free(debug_attrs_len); + + strcpy(ptr, "</device>"); + *length = ptr - str + sizeof("</device>") - 1; + return str; + +err_free_debug_attrs: + while (k--) + free(debug_attrs[k]); + free(debug_attrs); +err_free_debug_attrs_len: + free(debug_attrs_len); +err_free_buffer_attrs: + while (k--) + free(buffer_attrs[k]); + free(buffer_attrs); +err_free_buffer_attrs_len: + free(buffer_attrs_len); +err_free_channels: + while (j--) + free(channels[j]); + free(channels); +err_free_channels_len: + free(channels_len); +err_free_attrs: + while (i--) + free(attrs[i]); + free(attrs); +err_free_attrs_len: + free(attrs_len); + return NULL; +} + +const char * iio_device_get_id(const struct iio_device *dev) +{ + return dev->id; +} + +const char * iio_device_get_name(const struct iio_device *dev) +{ + return dev->name; +} + +unsigned int iio_device_get_channels_count(const struct iio_device *dev) +{ + return dev->nb_channels; +} + +struct iio_channel * iio_device_get_channel(const struct iio_device *dev, + unsigned int index) +{ + if (index >= dev->nb_channels) + return NULL; + else + return dev->channels[index]; +} + +struct iio_channel * iio_device_find_channel(const struct iio_device *dev, + const char *name, bool output) +{ + unsigned int i; + for (i = 0; i < dev->nb_channels; i++) { + struct iio_channel *chn = dev->channels[i]; + if (iio_channel_is_output(chn) != output) + continue; + + if (!strcmp(chn->id, name) || + (chn->name && !strcmp(chn->name, name))) + return chn; + } + return NULL; +} + +unsigned int iio_device_get_attrs_count(const struct iio_device *dev) +{ + return dev->nb_attrs; +} + +const char * iio_device_get_attr(const struct iio_device *dev, + unsigned int index) +{ + if (index >= dev->nb_attrs) + return NULL; + else + return dev->attrs[index]; +} + +const char * iio_device_find_attr(const struct iio_device *dev, + const char *name) +{ + unsigned int i; + for (i = 0; i < dev->nb_attrs; i++) { + const char *attr = dev->attrs[i]; + if (!strcmp(attr, name)) + return attr; + } + return NULL; +} + +unsigned int iio_device_get_buffer_attrs_count(const struct iio_device *dev) +{ + return dev->nb_buffer_attrs; +} + +const char * iio_device_get_buffer_attr(const struct iio_device *dev, + unsigned int index) +{ + if (index >= dev->nb_buffer_attrs) + return NULL; + else + return dev->buffer_attrs[index]; +} + +const char * iio_device_find_buffer_attr(const struct iio_device *dev, + const char *name) +{ + unsigned int i; + for (i = 0; i < dev->nb_buffer_attrs; i++) { + const char *attr = dev->buffer_attrs[i]; + if (!strcmp(attr, name)) + return attr; + } + return NULL; +} + +const char * iio_device_find_debug_attr(const struct iio_device *dev, + const char *name) +{ + unsigned int i; + for (i = 0; i < dev->nb_debug_attrs; i++) { + const char *attr = dev->debug_attrs[i]; + if (!strcmp(attr, name)) + return attr; + } + return NULL; +} + +bool iio_device_is_tx(const struct iio_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->nb_channels; i++) { + struct iio_channel *ch = dev->channels[i]; + if (iio_channel_is_output(ch) && iio_channel_is_enabled(ch)) + return true; + } + + return false; +} + +int iio_device_open(const struct iio_device *dev, + size_t samples_count, bool cyclic) +{ + unsigned int i; + bool has_channels = false; + + for (i = 0; !has_channels && i < dev->words; i++) + has_channels = !!dev->mask[i]; + if (!has_channels) + return -EINVAL; + + if (dev->ctx->ops->open) + return dev->ctx->ops->open(dev, samples_count, cyclic); + else + return -ENOSYS; +} + +int iio_device_close(const struct iio_device *dev) +{ + if (dev->ctx->ops->close) + return dev->ctx->ops->close(dev); + else + return -ENOSYS; +} + +int iio_device_get_poll_fd(const struct iio_device *dev) +{ + if (dev->ctx->ops->get_fd) + return dev->ctx->ops->get_fd(dev); + else + return -ENOSYS; +} + +int iio_device_set_blocking_mode(const struct iio_device *dev, bool blocking) +{ + if (dev->ctx->ops->set_blocking_mode) + return dev->ctx->ops->set_blocking_mode(dev, blocking); + else + return -ENOSYS; +} + +ssize_t iio_device_read_raw(const struct iio_device *dev, + void *dst, size_t len, uint32_t *mask, size_t words) +{ + if (dev->ctx->ops->read) + return dev->ctx->ops->read(dev, dst, len, mask, words); + else + return -ENOSYS; +} + +ssize_t iio_device_write_raw(const struct iio_device *dev, + const void *src, size_t len) +{ + if (dev->ctx->ops->write) + return dev->ctx->ops->write(dev, src, len); + else + return -ENOSYS; +} + +ssize_t iio_device_attr_read(const struct iio_device *dev, + const char *attr, char *dst, size_t len) +{ + if (dev->ctx->ops->read_device_attr) + return dev->ctx->ops->read_device_attr(dev, + attr, dst, len, IIO_ATTR_TYPE_DEVICE); + else + return -ENOSYS; +} + +ssize_t iio_device_attr_write_raw(const struct iio_device *dev, + const char *attr, const void *src, size_t len) +{ + if (dev->ctx->ops->write_device_attr) + return dev->ctx->ops->write_device_attr(dev, + attr, src, len, IIO_ATTR_TYPE_DEVICE); + else + return -ENOSYS; +} + +ssize_t iio_device_attr_write(const struct iio_device *dev, + const char *attr, const char *src) +{ + return iio_device_attr_write_raw(dev, attr, src, strlen(src) + 1); +} + +ssize_t iio_device_buffer_attr_read(const struct iio_device *dev, + const char *attr, char *dst, size_t len) +{ + if (dev->ctx->ops->read_device_attr) + return dev->ctx->ops->read_device_attr(dev, + attr, dst, len, IIO_ATTR_TYPE_BUFFER); + else + return -ENOSYS; +} + +ssize_t iio_device_buffer_attr_write_raw(const struct iio_device *dev, + const char *attr, const void *src, size_t len) +{ + if (dev->ctx->ops->write_device_attr) + return dev->ctx->ops->write_device_attr(dev, + attr, src, len, IIO_ATTR_TYPE_BUFFER); + else + return -ENOSYS; +} + +ssize_t iio_device_buffer_attr_write(const struct iio_device *dev, + const char *attr, const char *src) +{ + return iio_device_buffer_attr_write_raw(dev, attr, src, strlen(src) + 1); +} + +void iio_device_set_data(struct iio_device *dev, void *data) +{ + dev->userdata = data; +} + +void * iio_device_get_data(const struct iio_device *dev) +{ + return dev->userdata; +} + +bool iio_device_is_trigger(const struct iio_device *dev) +{ + /* A trigger has a name, an id which starts by "trigger", + * and zero channels. */ + + unsigned int nb = iio_device_get_channels_count(dev); + const char *name = iio_device_get_name(dev), + *id = iio_device_get_id(dev); + return ((nb == 0) && !!name && + !strncmp(id, "trigger", sizeof("trigger") - 1)); +} + +int iio_device_set_kernel_buffers_count(const struct iio_device *dev, + unsigned int nb_buffers) +{ + if (nb_buffers == 0) + return -EINVAL; + else if (dev->ctx->ops->set_kernel_buffers_count) + return dev->ctx->ops->set_kernel_buffers_count(dev, nb_buffers); + else + return -ENOSYS; +} + +int iio_device_get_trigger(const struct iio_device *dev, + const struct iio_device **trigger) +{ + if (!trigger) + return -EINVAL; + else if (dev->ctx->ops->get_trigger) + return dev->ctx->ops->get_trigger(dev, trigger); + else + return -ENOSYS; +} + +int iio_device_set_trigger(const struct iio_device *dev, + const struct iio_device *trigger) +{ + if (trigger && !iio_device_is_trigger(trigger)) + return -EINVAL; + else if (dev->ctx->ops->set_trigger) + return dev->ctx->ops->set_trigger(dev, trigger); + else + return -ENOSYS; +} + +void free_device(struct iio_device *dev) +{ + unsigned int i; + for (i = 0; i < dev->nb_attrs; i++) + free(dev->attrs[i]); + if (dev->nb_attrs) + free(dev->attrs); + for (i = 0; i < dev->nb_buffer_attrs; i++) + free(dev->buffer_attrs[i]); + if (dev->nb_buffer_attrs) + free(dev->buffer_attrs); + for (i = 0; i < dev->nb_debug_attrs; i++) + free(dev->debug_attrs[i]); + if (dev->nb_debug_attrs) + free(dev->debug_attrs); + for (i = 0; i < dev->nb_channels; i++) + free_channel(dev->channels[i]); + if (dev->nb_channels) + free(dev->channels); + if (dev->mask) + free(dev->mask); + if (dev->name) + free(dev->name); + if (dev->id) + free(dev->id); + free(dev); +} + +ssize_t iio_device_get_sample_size_mask(const struct iio_device *dev, + const uint32_t *mask, size_t words) +{ + ssize_t size = 0; + unsigned int i; + const struct iio_channel *prev = NULL; + + if (words != (dev->nb_channels + 31) / 32) + return -EINVAL; + + for (i = 0; i < dev->nb_channels; i++) { + const struct iio_channel *chn = dev->channels[i]; + unsigned int length = chn->format.length / 8 * + chn->format.repeat; + + if (chn->index < 0) + break; + if (!TEST_BIT(mask, chn->number)) + continue; + + if (prev && chn->index == prev->index) { + prev = chn; + continue; + } + + if (size % length) + size += 2 * length - (size % length); + else + size += length; + + prev = chn; + } + return size; +} + +ssize_t iio_device_get_sample_size(const struct iio_device *dev) +{ + return iio_device_get_sample_size_mask(dev, dev->mask, dev->words); +} + +int iio_device_attr_read_longlong(const struct iio_device *dev, + const char *attr, long long *val) +{ + char *end, buf[1024]; + long long value; + ssize_t ret = iio_device_attr_read(dev, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + + value = strtoll(buf, &end, 0); + if (end == buf) + return -EINVAL; + *val = value; + return 0; +} + +int iio_device_attr_read_bool(const struct iio_device *dev, + const char *attr, bool *val) +{ + long long value; + int ret = iio_device_attr_read_longlong(dev, attr, &value); + if (ret < 0) + return ret; + + *val = !!value; + return 0; +} + +int iio_device_attr_read_double(const struct iio_device *dev, + const char *attr, double *val) +{ + char buf[1024]; + ssize_t ret = iio_device_attr_read(dev, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + else + return read_double(buf, val); +} + +int iio_device_attr_write_longlong(const struct iio_device *dev, + const char *attr, long long val) +{ + ssize_t ret; + char buf[1024]; + + iio_snprintf(buf, sizeof(buf), "%lld", val); + ret = iio_device_attr_write(dev, attr, buf); + + return ret < 0 ? ret : 0; +} + +int iio_device_attr_write_double(const struct iio_device *dev, + const char *attr, double val) +{ + ssize_t ret; + char buf[1024]; + + ret = (ssize_t) write_double(buf, sizeof(buf), val); + if (!ret) + ret = iio_device_attr_write(dev, attr, buf); + return ret < 0 ? ret : 0; +} + +int iio_device_attr_write_bool(const struct iio_device *dev, + const char *attr, bool val) +{ + ssize_t ret; + + if (val) + ret = iio_device_attr_write(dev, attr, "1"); + else + ret = iio_device_attr_write(dev, attr, "0"); + + return ret < 0 ? ret : 0; +} + +int iio_device_buffer_attr_read_longlong(const struct iio_device *dev, + const char *attr, long long *val) +{ + char *end, buf[1024]; + long long value; + ssize_t ret = iio_device_buffer_attr_read(dev, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + + value = strtoll(buf, &end, 0); + if (end == buf) + return -EINVAL; + *val = value; + return 0; +} + +int iio_device_buffer_attr_read_bool(const struct iio_device *dev, + const char *attr, bool *val) +{ + long long value; + int ret = iio_device_buffer_attr_read_longlong(dev, attr, &value); + if (ret < 0) + return ret; + + *val = !!value; + return 0; +} + +int iio_device_buffer_attr_read_double(const struct iio_device *dev, + const char *attr, double *val) +{ + char buf[1024]; + ssize_t ret = iio_device_buffer_attr_read(dev, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + else + return read_double(buf, val); +} + +int iio_device_buffer_attr_write_longlong(const struct iio_device *dev, + const char *attr, long long val) +{ + ssize_t ret; + char buf[1024]; + + iio_snprintf(buf, sizeof(buf), "%lld", val); + ret = iio_device_buffer_attr_write(dev, attr, buf); + + return ret < 0 ? ret : 0; +} + +int iio_device_buffer_attr_write_double(const struct iio_device *dev, + const char *attr, double val) +{ + ssize_t ret; + char buf[1024]; + + ret = (ssize_t) write_double(buf, sizeof(buf), val); + if (!ret) + ret = iio_device_buffer_attr_write(dev, attr, buf); + return ret < 0 ? ret : 0; +} + +int iio_device_buffer_attr_write_bool(const struct iio_device *dev, + const char *attr, bool val) +{ + ssize_t ret; + + if (val) + ret = iio_device_buffer_attr_write(dev, attr, "1"); + else + ret = iio_device_buffer_attr_write(dev, attr, "0"); + + return ret < 0 ? ret : 0; +} + +ssize_t iio_device_debug_attr_read(const struct iio_device *dev, + const char *attr, char *dst, size_t len) +{ + if (dev->ctx->ops->read_device_attr) + return dev->ctx->ops->read_device_attr(dev, + attr, dst, len, IIO_ATTR_TYPE_DEBUG); + else + return -ENOSYS; +} + +ssize_t iio_device_debug_attr_write_raw(const struct iio_device *dev, + const char *attr, const void *src, size_t len) +{ + if (dev->ctx->ops->write_device_attr) + return dev->ctx->ops->write_device_attr(dev, + attr, src, len, IIO_ATTR_TYPE_DEBUG); + else + return -ENOSYS; +} + +ssize_t iio_device_debug_attr_write(const struct iio_device *dev, + const char *attr, const char *src) +{ + return iio_device_debug_attr_write_raw(dev, attr, src, strlen(src) + 1); +} + +unsigned int iio_device_get_debug_attrs_count(const struct iio_device *dev) +{ + return dev->nb_debug_attrs; +} + +const char * iio_device_get_debug_attr(const struct iio_device *dev, + unsigned int index) +{ + if (index >= dev->nb_debug_attrs) + return NULL; + else + return dev->debug_attrs[index]; +} + +int iio_device_debug_attr_read_longlong(const struct iio_device *dev, + const char *attr, long long *val) +{ + char *end, buf[1024]; + long long value; + ssize_t ret = iio_device_debug_attr_read(dev, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + + value = strtoll(buf, &end, 0); + if (end == buf) + return -EINVAL; + *val = value; + return 0; +} + +int iio_device_debug_attr_read_bool(const struct iio_device *dev, + const char *attr, bool *val) +{ + long long value; + int ret = iio_device_debug_attr_read_longlong(dev, attr, &value); + if (ret < 0) + return ret; + + *val = !!value; + return 0; +} + +int iio_device_debug_attr_read_double(const struct iio_device *dev, + const char *attr, double *val) +{ + char buf[1024]; + ssize_t ret = iio_device_debug_attr_read(dev, attr, buf, sizeof(buf)); + if (ret < 0) + return (int) ret; + else + return read_double(buf, val); +} + +int iio_device_debug_attr_write_longlong(const struct iio_device *dev, + const char *attr, long long val) +{ + ssize_t ret; + char buf[1024]; + + iio_snprintf(buf, sizeof(buf), "%lld", val); + ret = iio_device_debug_attr_write(dev, attr, buf); + + return ret < 0 ? ret : 0; +} + +int iio_device_debug_attr_write_double(const struct iio_device *dev, + const char *attr, double val) +{ + ssize_t ret; + char buf[1024]; + + ret = (ssize_t) write_double(buf, sizeof(buf), val); + if (!ret) + ret = iio_device_debug_attr_write(dev, attr, buf); + return ret < 0 ? ret : 0; +} + +int iio_device_debug_attr_write_bool(const struct iio_device *dev, + const char *attr, bool val) +{ + ssize_t ret; + + if (val) + ret = iio_device_debug_attr_write_raw(dev, attr, "1", 2); + else + ret = iio_device_debug_attr_write_raw(dev, attr, "0", 2); + + return ret < 0 ? ret : 0; +} + +int iio_device_identify_filename(const struct iio_device *dev, + const char *filename, struct iio_channel **chn, + const char **attr) +{ + unsigned int i; + + for (i = 0; i < dev->nb_channels; i++) { + struct iio_channel *ch = dev->channels[i]; + unsigned int j; + + for (j = 0; j < ch->nb_attrs; j++) { + if (!strcmp(ch->attrs[j].filename, filename)) { + *attr = ch->attrs[j].name; + *chn = ch; + return 0; + } + } + } + + for (i = 0; i < dev->nb_attrs; i++) { + /* Devices attributes are named after their filename */ + if (!strcmp(dev->attrs[i], filename)) { + *attr = dev->attrs[i]; + *chn = NULL; + return 0; + } + } + + for (i = 0; i < dev->nb_debug_attrs; i++) { + if (!strcmp(dev->debug_attrs[i], filename)) { + *attr = dev->debug_attrs[i]; + *chn = NULL; + return 0; + } + } + + return -EINVAL; +} + +int iio_device_reg_write(struct iio_device *dev, + uint32_t address, uint32_t value) +{ + ssize_t ret; + char buf[1024]; + + iio_snprintf(buf, sizeof(buf), "0x%" PRIx32 " 0x%" PRIx32, + address, value); + ret = iio_device_debug_attr_write(dev, "direct_reg_access", buf); + + return ret < 0 ? ret : 0; +} + +int iio_device_reg_read(struct iio_device *dev, + uint32_t address, uint32_t *value) +{ + /* NOTE: There is a race condition here. But it is extremely unlikely to + * happen, and as this is a debug function, it shouldn't be used for + * something else than debug. */ + + long long val; + int ret = iio_device_debug_attr_write_longlong(dev, + "direct_reg_access", (long long) address); + if (ret < 0) + return ret; + + ret = iio_device_debug_attr_read_longlong(dev, + "direct_reg_access", &val); + if (!ret) + *value = (uint32_t) val; + return ret; +} + +static int read_each_attr(struct iio_device *dev, enum iio_attr_type type, + int (*cb)(struct iio_device *dev, + const char *attr, const char *val, size_t len, void *d), + void *data) +{ + int ret, buf_size; + char *buf, *ptr; + unsigned int i, count; + + /* We need a big buffer here; 1 MiB should be enough */ + buf = malloc(0x100000); + if (!buf) + return -ENOMEM; + + switch(type){ + case IIO_ATTR_TYPE_DEVICE: + count = iio_device_get_attrs_count(dev); + ret = (int) iio_device_attr_read(dev, + NULL, buf, 0x100000); + break; + case IIO_ATTR_TYPE_DEBUG: + count = iio_device_get_debug_attrs_count(dev); + ret = (int) iio_device_debug_attr_read(dev, + NULL, buf, 0x100000); + break; + case IIO_ATTR_TYPE_BUFFER: + count = iio_device_get_buffer_attrs_count(dev); + ret = (int) iio_device_buffer_attr_read(dev, + NULL, buf, 0x100000); + break; + default: + ret = -EINVAL; + count = 0; + break; + } + + if (ret < 0) + goto err_free_buf; + + ptr = buf; + buf_size = ret; + + for (i = 0; i < count; i++) { + const char *attr; + int32_t len; + + if (buf_size < 4) { + ret = -EPROTO; + break; + } + + len = (int32_t) iio_be32toh(*(uint32_t *) ptr); + ptr += 4; + buf_size -= 4; + + if (len > 0 && buf_size < len) { + ret = -EPROTO; + break; + } + + switch(type){ + case IIO_ATTR_TYPE_DEVICE: + attr = iio_device_get_attr(dev, i); + break; + case IIO_ATTR_TYPE_DEBUG: + attr = iio_device_get_debug_attr(dev, i); + break; + case IIO_ATTR_TYPE_BUFFER: + attr = iio_device_get_buffer_attr(dev, i); + break; + default: + attr = NULL; + break; + } + + if (len > 0) { + ret = cb(dev, attr, ptr, (size_t) len, data); + if (ret < 0) + goto err_free_buf; + + if (len & 0x3) + len = ((len >> 2) + 1) << 2; + ptr += len; + if (len >= buf_size) + buf_size = 0; + else + buf_size -= len; + } + } + +err_free_buf: + free(buf); + return ret < 0 ? ret : 0; +} + +static int write_each_attr(struct iio_device *dev, enum iio_attr_type type, + ssize_t (*cb)(struct iio_device *dev, + const char *attr, void *buf, size_t len, void *d), + void *data) +{ + char *buf, *ptr; + unsigned int i, count; + size_t len = 0x100000; + int ret; + + /* We need a big buffer here; 1 MiB should be enough */ + buf = malloc(len); + if (!buf) + return -ENOMEM; + + ptr = buf; + + switch(type){ + case IIO_ATTR_TYPE_DEVICE: + count = iio_device_get_attrs_count(dev); + break; + case IIO_ATTR_TYPE_DEBUG: + count = iio_device_get_debug_attrs_count(dev); + break; + case IIO_ATTR_TYPE_BUFFER: + count = iio_device_get_buffer_attrs_count(dev); + break; + default: + ret = -EINVAL; + goto err_free_buf; + } + + for (i = 0; i < count; i++) { + const char *attr; + + switch(type){ + case IIO_ATTR_TYPE_DEVICE: + attr = iio_device_get_attr(dev, i); + break; + case IIO_ATTR_TYPE_DEBUG: + attr = iio_device_get_debug_attr(dev, i); + break; + case IIO_ATTR_TYPE_BUFFER: + attr = iio_device_get_buffer_attr(dev, i); + break; + default: + attr = NULL; + break; + } + + ret = (int) cb(dev, attr, ptr + 4, len - 4, data); + if (ret < 0) + goto err_free_buf; + + *(int32_t *) ptr = (int32_t) iio_htobe32((uint32_t) ret); + ptr += 4; + len -= 4; + + if (ret > 0) { + if (ret & 0x3) + ret = ((ret >> 2) + 1) << 2; + ptr += ret; + len -= ret; + } + } + + switch(type){ + case IIO_ATTR_TYPE_DEVICE: + ret = (int) iio_device_attr_write_raw(dev, + NULL, buf, ptr - buf); + break; + case IIO_ATTR_TYPE_DEBUG: + ret = (int) iio_device_debug_attr_write_raw(dev, + NULL, buf, ptr - buf); + break; + case IIO_ATTR_TYPE_BUFFER: + ret = (int) iio_device_buffer_attr_write_raw(dev, + NULL, buf, ptr - buf); + break; + default: + ret = -EINVAL; + break; + } + +err_free_buf: + free(buf); + return ret < 0 ? ret : 0; +} + +int iio_device_debug_attr_read_all(struct iio_device *dev, + int (*cb)(struct iio_device *dev, + const char *attr, const char *val, size_t len, void *d), + void *data) +{ + return read_each_attr(dev, IIO_ATTR_TYPE_DEBUG, cb, data); +} + +int iio_device_buffer_attr_read_all(struct iio_device *dev, + int (*cb)(struct iio_device *dev, + const char *attr, const char *val, size_t len, void *d), + void *data) +{ + return read_each_attr(dev, IIO_ATTR_TYPE_BUFFER, cb, data); +} + +int iio_device_attr_read_all(struct iio_device *dev, + int (*cb)(struct iio_device *dev, + const char *attr, const char *val, size_t len, void *d), + void *data) +{ + return read_each_attr(dev, IIO_ATTR_TYPE_DEVICE ,cb, data); +} + +int iio_device_debug_attr_write_all(struct iio_device *dev, + ssize_t (*cb)(struct iio_device *dev, + const char *attr, void *buf, size_t len, void *d), + void *data) +{ + return write_each_attr(dev, IIO_ATTR_TYPE_DEBUG, cb, data); +} + +int iio_device_buffer_attr_write_all(struct iio_device *dev, + ssize_t (*cb)(struct iio_device *dev, + const char *attr, void *buf, size_t len, void *d), + void *data) +{ + return write_each_attr(dev, IIO_ATTR_TYPE_BUFFER, cb, data); +} + +int iio_device_attr_write_all(struct iio_device *dev, + ssize_t (*cb)(struct iio_device *dev, + const char *attr, void *buf, size_t len, void *d), + void *data) +{ + return write_each_attr(dev, IIO_ATTR_TYPE_DEVICE, cb, data); +} + +const struct iio_context * iio_device_get_context(const struct iio_device *dev) +{ + return dev->ctx; +} |