Paul Cercueil / libiio

Dependencies:   libserialport libxml2

Revision:
0:df031b60ca29
Child:
2:9eb0a9a1f958
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/channel.c	Thu Aug 25 15:54:09 2016 +0000
@@ -0,0 +1,805 @@
+/*
+ * 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-errno.h"
+#include "iio-private.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static const char * const iio_chan_type_name_spec[] = {
+	[IIO_VOLTAGE] = "voltage",
+	[IIO_CURRENT] = "current",
+	[IIO_POWER] = "power",
+	[IIO_ACCEL] = "accel",
+	[IIO_ANGL_VEL] = "anglvel",
+	[IIO_MAGN] = "magn",
+	[IIO_LIGHT] = "illuminance",
+	[IIO_INTENSITY] = "intensity",
+	[IIO_PROXIMITY] = "proximity",
+	[IIO_TEMP] = "temp",
+	[IIO_INCLI] = "incli",
+	[IIO_ROT] = "rot",
+	[IIO_ANGL] = "angl",
+	[IIO_TIMESTAMP] = "timestamp",
+	[IIO_CAPACITANCE] = "capacitance",
+	[IIO_ALTVOLTAGE] = "altvoltage",
+	[IIO_CCT] = "cct",
+	[IIO_PRESSURE] = "pressure",
+	[IIO_HUMIDITYRELATIVE] = "humidityrelative",
+	[IIO_ACTIVITY] = "activity",
+	[IIO_STEPS] = "steps",
+	[IIO_ENERGY] = "energy",
+	[IIO_DISTANCE] = "distance",
+	[IIO_VELOCITY] = "velocity",
+	[IIO_CONCENTRATION] = "concentration",
+	[IIO_RESISTANCE] = "resistance",
+	[IIO_PH] = "ph",
+};
+
+static const char * const modifier_names[] = {
+	[IIO_MOD_X] = "x",
+	[IIO_MOD_Y] = "y",
+	[IIO_MOD_Z] = "z",
+	[IIO_MOD_X_AND_Y] = "x&y",
+	[IIO_MOD_X_AND_Z] = "x&z",
+	[IIO_MOD_Y_AND_Z] = "y&z",
+	[IIO_MOD_X_AND_Y_AND_Z] = "x&y&z",
+	[IIO_MOD_X_OR_Y] = "x|y",
+	[IIO_MOD_X_OR_Z] = "x|z",
+	[IIO_MOD_Y_OR_Z] = "y|z",
+	[IIO_MOD_X_OR_Y_OR_Z] = "x|y|z",
+	[IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)",
+	[IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2",
+	[IIO_MOD_LIGHT_BOTH] = "both",
+	[IIO_MOD_LIGHT_IR] = "ir",
+	[IIO_MOD_LIGHT_CLEAR] = "clear",
+	[IIO_MOD_LIGHT_RED] = "red",
+	[IIO_MOD_LIGHT_GREEN] = "green",
+	[IIO_MOD_LIGHT_BLUE] = "blue",
+	[IIO_MOD_QUATERNION] = "quaternion",
+	[IIO_MOD_TEMP_AMBIENT] = "ambient",
+	[IIO_MOD_TEMP_OBJECT] = "object",
+	[IIO_MOD_NORTH_MAGN] = "from_north_magnetic",
+	[IIO_MOD_NORTH_TRUE] = "from_north_true",
+	[IIO_MOD_NORTH_MAGN_TILT_COMP] = "from_north_magnetic_tilt_comp",
+	[IIO_MOD_NORTH_TRUE_TILT_COMP] = "from_north_true_tilt_comp",
+	[IIO_MOD_RUNNING] = "running",
+	[IIO_MOD_JOGGING] = "jogging",
+	[IIO_MOD_WALKING] = "walking",
+	[IIO_MOD_STILL] = "still",
+	[IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
+	[IIO_MOD_I] = "i",
+	[IIO_MOD_Q] = "q",
+	[IIO_MOD_CO2] = "co2",
+	[IIO_MOD_VOC] = "voc",
+};
+
+/*
+ * Looks for a IIO channel modifier at the beginning of the string s. If a
+ * modifier was found the symbolic constant (IIO_MOD_*) is returned, otherwise
+ * IIO_NO_MOD is returned. If a modifier was found len_p will be updated with
+ * the length of the modifier.
+ */
+unsigned int find_channel_modifier(const char *s, size_t *len_p)
+{
+	unsigned int i;
+	size_t len;
+
+	for (i = 0; i < ARRAY_SIZE(modifier_names); i++) {
+		if (!modifier_names[i])
+			continue;
+		len = strlen(modifier_names[i]);
+		if (strncmp(s, modifier_names[i], len) == 0 &&
+				(s[len] == '\0' || s[len] == '_')) {
+			if (len_p)
+				*len_p = len;
+			return i;
+		}
+	}
+
+	return IIO_NO_MOD;
+}
+
+/*
+ * Initializes all auto-detected fields of the channel struct. Must be called
+ * after the channel has been otherwise fully initialized.
+ */
+void iio_channel_init_finalize(struct iio_channel *chn)
+{
+	unsigned int i;
+	size_t len;
+	char *mod;
+
+	chn->type = IIO_CHAN_TYPE_UNKNOWN;
+	chn->modifier = IIO_NO_MOD;
+
+	for (i = 0; i < ARRAY_SIZE(iio_chan_type_name_spec); i++) {
+		len = strlen(iio_chan_type_name_spec[i]);
+		if (strncmp(iio_chan_type_name_spec[i], chn->id, len) != 0)
+			continue;
+		/* Type must be followed by either a '_' or a digit */
+		if (chn->id[len] != '_' && chn->id[len] < '0' && chn->id[len] > '9')
+			continue;
+
+		chn->type = (enum iio_chan_type) i;
+	}
+
+	mod = strchr(chn->id, '_');
+	if (!mod)
+		return;
+
+	mod++;
+
+	for (i = 0; i < ARRAY_SIZE(modifier_names); i++) {
+		if (!modifier_names[i])
+			continue;
+		len = strlen(modifier_names[i]);
+		if (strncmp(modifier_names[i], mod, len) != 0)
+			continue;
+		/* Modifier must be followed by a '_' */
+		if (mod[len] != '_')
+			continue;
+
+		chn->modifier = (enum iio_modifier) i;
+		break;
+	}
+}
+
+static char *get_attr_xml(struct iio_channel_attr *attr, size_t *length)
+{
+	char *str;
+	size_t len = strlen(attr->name) + sizeof("<attribute name=\"\" />");
+	if (attr->filename)
+		len += strlen(attr->filename) + sizeof("filename=\"\"");
+
+	str = malloc(len);
+	if (!str)
+		return NULL;
+
+	*length = len - 1; /* Skip the \0 */
+	if (attr->filename)
+		snprintf(str, len, "<attribute name=\"%s\" filename=\"%s\" />",
+				attr->name, attr->filename);
+	else
+		snprintf(str, len, "<attribute name=\"%s\" />", attr->name);
+	return str;
+}
+
+static char * get_scan_element(const struct iio_channel *chn, size_t *length)
+{
+	char buf[1024], *str;
+	char processed = (chn->format.is_fully_defined ? 'A' - 'a' : 0);
+
+	snprintf(buf, sizeof(buf), "<scan-element index=\"%li\" "
+			"format=\"%ce:%c%u/%u&gt;&gt;%u\" />",
+			chn->index, chn->format.is_be ? 'b' : 'l',
+			chn->format.is_signed ? 's' + processed : 'u' + processed,
+			chn->format.bits, chn->format.length,
+			chn->format.shift);
+
+	if (chn->format.with_scale) {
+		char *ptr = strrchr(buf, '\0');
+		snprintf(ptr - 2, buf + sizeof(buf) - ptr + 2,
+				"scale=\"%f\" />", chn->format.scale);
+	}
+
+	str = iio_strdup(buf);
+	if (str)
+		*length = strlen(str);
+	return str;
+}
+
+/* Returns a string containing the XML representation of this channel */
+char * iio_channel_get_xml(const struct iio_channel *chn, size_t *length)
+{
+	size_t len = sizeof("<channel id=\"\" name=\"\" "
+			"type=\"output\" ></channel>")
+		+ strlen(chn->id) + (chn->name ? strlen(chn->name) : 0);
+	char *ptr, *str, **attrs, *scan_element = NULL;
+	size_t *attrs_len, scan_element_len = 0;
+	unsigned int i;
+
+	if (chn->is_scan_element) {
+		scan_element = get_scan_element(chn, &scan_element_len);
+		if (!scan_element)
+			return NULL;
+		else
+			len += scan_element_len;
+	}
+
+	attrs_len = malloc(chn->nb_attrs * sizeof(*attrs_len));
+	if (!attrs_len)
+		goto err_free_scan_element;
+
+	attrs = malloc(chn->nb_attrs * sizeof(*attrs));
+	if (!attrs)
+		goto err_free_attrs_len;
+
+	for (i = 0; i < chn->nb_attrs; i++) {
+		char *xml = get_attr_xml(&chn->attrs[i], &attrs_len[i]);
+		if (!xml)
+			goto err_free_attrs;
+		attrs[i] = xml;
+		len += attrs_len[i];
+	}
+
+	str = malloc(len);
+	if (!str)
+		goto err_free_attrs;
+
+	snprintf(str, len, "<channel id=\"%s\"", chn->id);
+	ptr = strrchr(str, '\0');
+
+	if (chn->name) {
+		sprintf(ptr, " name=\"%s\"", chn->name);
+		ptr = strrchr(ptr, '\0');
+	}
+
+	sprintf(ptr, " type=\"%s\" >", chn->is_output ? "output" : "input");
+	ptr = strrchr(ptr, '\0');
+
+	if (chn->is_scan_element) {
+		strcpy(ptr, scan_element);
+		ptr += scan_element_len;
+	}
+
+	for (i = 0; i < chn->nb_attrs; i++) {
+		strcpy(ptr, attrs[i]);
+		ptr += attrs_len[i];
+		free(attrs[i]);
+	}
+
+	free(scan_element);
+	free(attrs);
+	free(attrs_len);
+
+	strcpy(ptr, "</channel>");
+	*length = ptr - str + sizeof("</channel>") - 1;
+	return str;
+
+err_free_attrs:
+	while (i--)
+		free(attrs[i]);
+	free(attrs);
+err_free_attrs_len:
+	free(attrs_len);
+err_free_scan_element:
+	if (chn->is_scan_element)
+		free(scan_element);
+	return NULL;
+}
+
+const char * iio_channel_get_id(const struct iio_channel *chn)
+{
+	return chn->id;
+}
+
+const char * iio_channel_get_name(const struct iio_channel *chn)
+{
+	return chn->name;
+}
+
+bool iio_channel_is_output(const struct iio_channel *chn)
+{
+	return chn->is_output;
+}
+
+bool iio_channel_is_scan_element(const struct iio_channel *chn)
+{
+	return chn->is_scan_element;
+}
+
+enum iio_modifier iio_channel_get_modifier(const struct iio_channel *chn)
+{
+	return chn->modifier;
+}
+
+enum iio_chan_type iio_channel_get_type(const struct iio_channel *chn)
+{
+	return chn->type;
+}
+
+unsigned int iio_channel_get_attrs_count(const struct iio_channel *chn)
+{
+	return chn->nb_attrs;
+}
+
+const char * iio_channel_get_attr(const struct iio_channel *chn,
+		unsigned int index)
+{
+	if (index >= chn->nb_attrs)
+		return NULL;
+	else
+		return chn->attrs[index].name;
+}
+
+const char * iio_channel_find_attr(const struct iio_channel *chn,
+		const char *name)
+{
+	unsigned int i;
+	for (i = 0; i < chn->nb_attrs; i++) {
+		const char *attr = chn->attrs[i].name;
+		if (!strcmp(attr, name))
+			return attr;
+	}
+	return NULL;
+}
+
+ssize_t iio_channel_attr_read(const struct iio_channel *chn,
+		const char *attr, char *dst, size_t len)
+{
+	if (chn->dev->ctx->ops->read_channel_attr)
+		return chn->dev->ctx->ops->read_channel_attr(chn,
+				attr, dst, len);
+	else
+		return -ENOSYS;
+}
+
+ssize_t iio_channel_attr_write_raw(const struct iio_channel *chn,
+		const char *attr, const void *src, size_t len)
+{
+	if (chn->dev->ctx->ops->write_channel_attr)
+		return chn->dev->ctx->ops->write_channel_attr(chn,
+				attr, src, len);
+	else
+		return -ENOSYS;
+}
+
+ssize_t iio_channel_attr_write(const struct iio_channel *chn,
+		const char *attr, const char *src)
+{
+	return iio_channel_attr_write_raw(chn, attr, src, strlen(src) + 1);
+}
+
+void iio_channel_set_data(struct iio_channel *chn, void *data)
+{
+	chn->userdata = data;
+}
+
+void * iio_channel_get_data(const struct iio_channel *chn)
+{
+	return chn->userdata;
+}
+
+long iio_channel_get_index(const struct iio_channel *chn)
+{
+	return chn->index;
+}
+
+const struct iio_data_format * iio_channel_get_data_format(
+		const struct iio_channel *chn)
+{
+	return &chn->format;
+}
+
+bool iio_channel_is_enabled(const struct iio_channel *chn)
+{
+	return chn->index >= 0 && chn->dev->mask &&
+		TEST_BIT(chn->dev->mask, chn->index);
+}
+
+void iio_channel_enable(struct iio_channel *chn)
+{
+	if (chn->is_scan_element && chn->index >= 0 && chn->dev->mask)
+		SET_BIT(chn->dev->mask, chn->index);
+}
+
+void iio_channel_disable(struct iio_channel *chn)
+{
+	if (chn->index >= 0 && chn->dev->mask)
+		CLEAR_BIT(chn->dev->mask, chn->index);
+}
+
+void free_channel(struct iio_channel *chn)
+{
+	size_t i;
+	for (i = 0; i < chn->nb_attrs; i++) {
+		free(chn->attrs[i].name);
+		free(chn->attrs[i].filename);
+	}
+	if (chn->nb_attrs)
+		free(chn->attrs);
+	if (chn->name)
+		free(chn->name);
+	if (chn->id)
+		free(chn->id);
+	free(chn);
+}
+
+static void byte_swap(uint8_t *dst, const uint8_t *src, size_t len)
+{
+	size_t i;
+	for (i = 0; i < len; i++)
+		dst[i] = src[len - i - 1];
+}
+
+static void shift_bits(uint8_t *dst, size_t shift, size_t len, bool left)
+{
+	size_t i, shift_bytes = shift / 8;
+	shift %= 8;
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+	if (!left)
+#else
+	if (left)
+#endif
+	{
+		if (shift_bytes) {
+			memmove(dst, dst + shift_bytes, len - shift_bytes);
+			memset(dst + len - shift_bytes, 0, shift_bytes);
+		}
+		if (shift) {
+			for (i = 0; i < len; i++) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+				dst[i] >>= shift;
+				if (i < len - 1)
+					dst[i] |= dst[i + 1] << (8 - shift);
+#else
+				dst[i] <<= shift;
+				if (i < len - 1)
+					dst[i] |= dst[i + 1] >> (8 - shift);
+#endif
+			}
+		}
+	} else {
+		if (shift_bytes) {
+			memmove(dst + shift_bytes, dst, len - shift_bytes);
+			memset(dst, 0, shift_bytes);
+		}
+		if (shift) {
+			for (i = len; i > 0; i--) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+				dst[i - 1] <<= shift;
+				if (i > 1)
+					dst[i - 1] |= dst[i - 2] >> (8 - shift);
+#else
+				dst[i - 1] >>= shift;
+				if (i > 1)
+					dst[i - 1] |= dst[i - 2] << (8 - shift);
+#endif
+			}
+		}
+	}
+}
+
+static void sign_extend(uint8_t *dst, size_t bits, size_t len)
+{
+	size_t upper_bytes = ((len * 8 - bits) / 8);
+	uint8_t msb, msb_bit = 1 << ((bits - 1) % 8);
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+	msb = dst[len - 1 - upper_bytes] & msb_bit;
+	if (upper_bytes)
+		memset(dst + len - upper_bytes, msb ? 0xff : 0x00, upper_bytes);
+	if (msb)
+		dst[len - 1 - upper_bytes] |= ~(msb_bit - 1);
+	else
+		dst[len - 1 - upper_bytes] &= (msb_bit - 1);
+#else
+	/* XXX: untested */
+	msb = dst[upper_bytes] & msb_bit;
+	if (upper_bytes)
+		memset(dst, msb ? 0xff : 0x00, upper_bytes);
+	if (msb)
+		dst[upper_bytes] |= ~(msb_bit - 1);
+#endif
+}
+
+static void mask_upper_bits(uint8_t *dst, size_t bits, size_t len)
+{
+	size_t i;
+
+	/* Clear upper bits */
+	if (bits % 8)
+		dst[bits / 8] &= (1 << (bits % 8)) - 1;
+
+	/* Clear upper bytes */
+	for (i = (bits + 7) / 8; i < len; i++)
+		dst[i] = 0;
+}
+
+
+void iio_channel_convert(const struct iio_channel *chn,
+		void *dst, const void *src)
+{
+	unsigned int len = chn->format.length / 8;
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+	bool swap = chn->format.is_be;
+#else
+	bool swap = !chn->format.is_be;
+#endif
+
+	if (len == 1 || !swap)
+		memcpy(dst, src, len);
+	else
+		byte_swap(dst, src, len);
+
+	if (chn->format.shift)
+		shift_bits(dst, chn->format.shift, len, false);
+
+	if (!chn->format.is_fully_defined) {
+		if (chn->format.is_signed)
+			sign_extend(dst, chn->format.bits, len);
+		else
+			mask_upper_bits(dst, chn->format.bits, len);
+	}
+}
+
+void iio_channel_convert_inverse(const struct iio_channel *chn,
+		void *dst, const void *src)
+{
+	unsigned int len = chn->format.length / 8;
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+	bool swap = chn->format.is_be;
+#else
+	bool swap = !chn->format.is_be;
+#endif
+	uint8_t buf[1024];
+
+	/* Somehow I doubt we will have samples of 8192 bits each. */
+	if (len > sizeof(buf))
+		return;
+
+	memcpy(buf, src, len);
+	mask_upper_bits(buf, chn->format.bits, len);
+
+	if (chn->format.shift)
+		shift_bits(buf, chn->format.shift, len, true);
+
+	if (len == 1 || !swap)
+		memcpy(dst, buf, len);
+	else
+		byte_swap(dst, buf, len);
+}
+
+size_t iio_channel_read_raw(const struct iio_channel *chn,
+		struct iio_buffer *buf, void *dst, size_t len)
+{
+	uintptr_t src_ptr, dst_ptr = (uintptr_t) dst, end = dst_ptr + len;
+	unsigned int length = chn->format.length / 8;
+	uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf);
+	ptrdiff_t buf_step = iio_buffer_step(buf);
+
+	for (src_ptr = (uintptr_t) iio_buffer_first(buf, chn);
+			src_ptr < buf_end && dst_ptr + length <= end;
+			src_ptr += buf_step, dst_ptr += length)
+		memcpy((void *) dst_ptr, (const void *) src_ptr, length);
+	return dst_ptr - (uintptr_t) dst;
+}
+
+size_t iio_channel_read(const struct iio_channel *chn,
+		struct iio_buffer *buf, void *dst, size_t len)
+{
+	uintptr_t src_ptr, dst_ptr = (uintptr_t) dst, end = dst_ptr + len;
+	unsigned int length = chn->format.length / 8;
+	uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf);
+	ptrdiff_t buf_step = iio_buffer_step(buf);
+
+	for (src_ptr = (uintptr_t) iio_buffer_first(buf, chn);
+			src_ptr < buf_end && dst_ptr + length <= end;
+			src_ptr += buf_step, dst_ptr += length)
+		iio_channel_convert(chn,
+				(void *) dst_ptr, (const void *) src_ptr);
+	return dst_ptr - (uintptr_t) dst;
+}
+
+size_t iio_channel_write_raw(const struct iio_channel *chn,
+		struct iio_buffer *buf, const void *src, size_t len)
+{
+	uintptr_t dst_ptr, src_ptr = (uintptr_t) src, end = src_ptr + len;
+	unsigned int length = chn->format.length / 8;
+	uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf);
+	ptrdiff_t buf_step = iio_buffer_step(buf);
+
+	for (dst_ptr = (uintptr_t) iio_buffer_first(buf, chn);
+			dst_ptr < buf_end && src_ptr + length <= end;
+			dst_ptr += buf_step, src_ptr += length)
+		memcpy((void *) dst_ptr, (const void *) src_ptr, length);
+	return src_ptr - (uintptr_t) src;
+}
+
+size_t iio_channel_write(const struct iio_channel *chn,
+		struct iio_buffer *buf, const void *src, size_t len)
+{
+	uintptr_t dst_ptr, src_ptr = (uintptr_t) src, end = src_ptr + len;
+	unsigned int length = chn->format.length / 8;
+	uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf);
+	ptrdiff_t buf_step = iio_buffer_step(buf);
+
+	for (dst_ptr = (uintptr_t) iio_buffer_first(buf, chn);
+			dst_ptr < buf_end && src_ptr + length <= end;
+			dst_ptr += buf_step, src_ptr += length)
+		iio_channel_convert_inverse(chn,
+				(void *) dst_ptr, (const void *) src_ptr);
+	return src_ptr - (uintptr_t) src;
+}
+
+int iio_channel_attr_read_longlong(const struct iio_channel *chn,
+		const char *attr, long long *val)
+{
+	char *end, buf[1024];
+	long long value;
+	ssize_t ret = iio_channel_attr_read(chn, 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_channel_attr_read_bool(const struct iio_channel *chn,
+		const char *attr, bool *val)
+{
+	long long value;
+	int ret = iio_channel_attr_read_longlong(chn, attr, &value);
+	if (ret < 0)
+		return ret;
+
+	*val = !!value;
+	return 0;
+}
+
+int iio_channel_attr_read_double(const struct iio_channel *chn,
+		const char *attr, double *val)
+{
+	char buf[1024];
+	ssize_t ret = iio_channel_attr_read(chn, attr, buf, sizeof(buf));
+	if (ret < 0)
+		return (int) ret;
+	else
+		return read_double(buf, val);
+}
+
+int iio_channel_attr_write_longlong(const struct iio_channel *chn,
+		const char *attr, long long val)
+{
+	ssize_t ret;
+	char buf[1024];
+	snprintf(buf, sizeof(buf), "%lld", val);
+	ret = iio_channel_attr_write(chn, attr, buf);
+	return ret < 0 ? ret : 0;
+}
+
+int iio_channel_attr_write_double(const struct iio_channel *chn,
+		const char *attr, double val)
+{
+	ssize_t ret;
+	char buf[1024];
+
+	ret = (ssize_t) write_double(buf, sizeof(buf), val);
+	if (!ret)
+		ret = iio_channel_attr_write(chn, attr, buf);
+	return ret < 0 ? ret : 0;
+}
+
+int iio_channel_attr_write_bool(const struct iio_channel *chn,
+		const char *attr, bool val)
+{
+	ssize_t ret;
+	if (val)
+		ret = iio_channel_attr_write_raw(chn, attr, "1", 2);
+	else
+		ret = iio_channel_attr_write_raw(chn, attr, "0", 2);
+	return ret < 0 ? ret : 0;
+}
+
+const char * iio_channel_attr_get_filename(
+		const struct iio_channel *chn, const char *attr)
+{
+	unsigned int i;
+	for (i = 0; i < chn->nb_attrs; i++) {
+		if (!strcmp(chn->attrs[i].name, attr))
+			return chn->attrs[i].filename;
+	}
+	return NULL;
+}
+
+int iio_channel_attr_read_all(struct iio_channel *chn,
+		int (*cb)(struct iio_channel *chn,
+			const char *attr, const char *val, size_t len, void *d),
+		void *data)
+{
+	int ret;
+	char *buf, *ptr;
+	unsigned int i;
+
+	/* We need a big buffer here; 1 MiB should be enough */
+	buf = malloc(0x100000);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = (int) iio_channel_attr_read(chn, NULL, buf, 0x100000);
+	if (ret < 0)
+		goto err_free_buf;
+
+	ptr = buf;
+
+	for (i = 0; i < iio_channel_get_attrs_count(chn); i++) {
+		const char *attr = iio_channel_get_attr(chn, i);
+		int32_t len = (int32_t) iio_be32toh(*(uint32_t *) ptr);
+
+		ptr += 4;
+		if (len > 0) {
+			ret = cb(chn, attr, ptr, (size_t) len, data);
+			if (ret < 0)
+				goto err_free_buf;
+
+			if (len & 0x3)
+				len = ((len >> 2) + 1) << 2;
+			ptr += len;
+		}
+	}
+
+err_free_buf:
+	free(buf);
+	return ret < 0 ? ret : 0;
+}
+
+int iio_channel_attr_write_all(struct iio_channel *chn,
+		ssize_t (*cb)(struct iio_channel *chn,
+			const char *attr, void *buf, size_t len, void *d),
+		void *data)
+{
+	char *buf, *ptr;
+	unsigned int i;
+	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;
+
+	for (i = 0; i < iio_channel_get_attrs_count(chn); i++) {
+		const char *attr = iio_channel_get_attr(chn, i);
+
+		ret = (int) cb(chn, 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;
+		}
+	}
+
+	ret = (int) iio_channel_attr_write_raw(chn, NULL, buf, ptr - buf);
+
+err_free_buf:
+	free(buf);
+	return ret < 0 ? ret : 0;
+}
+
+const struct iio_device * iio_channel_get_device(const struct iio_channel *chn)
+{
+	return chn->dev;
+}
+