diff --git a/buffer.c b/buffer.c
index 7b84cd5bb..b58efb99d 100644
--- a/buffer.c
+++ b/buffer.c
@@ -236,7 +236,7 @@ ssize_t iio_buffer_foreach_sample(struct iio_buffer *buffer,
processed += ret;
}
- ptr += length;
+ ptr += length * chn->format.repeat;
}
}
return processed;
@@ -259,7 +259,7 @@ void * iio_buffer_first(const struct iio_buffer *buffer,
for (i = 0; i < buffer->dev->nb_channels; i++) {
struct iio_channel *cur = buffer->dev->channels[i];
- len = cur->format.length / 8;
+ len = cur->format.length / 8 * cur->format.repeat;
/* NOTE: dev->channels are ordered by index */
if (cur->index < 0 || cur->index == chn->index)
diff --git a/channel.c b/channel.c
index 54533fae6..16ac33624 100644
--- a/channel.c
+++ b/channel.c
@@ -184,14 +184,17 @@ static char *get_attr_xml(struct iio_channel_attr *attr, size_t *length)
static char * get_scan_element(const struct iio_channel *chn, size_t *length)
{
- char buf[1024], *str;
+ char buf[1024], repeat[8] = "", *str;
char processed = (chn->format.is_fully_defined ? 'A' - 'a' : 0);
+ if (chn->format.repeat > 1)
+ snprintf(repeat, sizeof(repeat), "X%u", chn->format.repeat);
+
snprintf(buf, sizeof(buf), "",
+ "format=\"%ce:%c%u/%u%s>>%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.bits, chn->format.length, repeat,
chn->format.shift);
if (chn->format.with_scale) {
@@ -519,33 +522,46 @@ static void mask_upper_bits(uint8_t *dst, size_t bits, size_t len)
void iio_channel_convert(const struct iio_channel *chn,
void *dst, const void *src)
{
+ uintptr_t src_ptr = (uintptr_t) src, dst_ptr = (uintptr_t) dst;
unsigned int len = chn->format.length / 8;
+ ptrdiff_t end = len * chn->format.repeat;
+ uintptr_t end_ptr = src_ptr + end;
#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);
+ for (src_ptr = (uintptr_t) src; src_ptr < end_ptr;
+ src_ptr += len, dst_ptr += len) {
+ if (len == 1 || !swap)
+ memcpy((void *) dst_ptr, (const void *) src_ptr, len);
else
- mask_upper_bits(dst, chn->format.bits, len);
+ byte_swap((void *) dst_ptr, (const void *) src_ptr,
+ len);
+
+ if (chn->format.shift)
+ shift_bits((void *) dst_ptr, chn->format.shift, len,
+ false);
+
+ if (!chn->format.is_fully_defined) {
+ if (chn->format.is_signed)
+ sign_extend((void *) dst_ptr,
+ chn->format.bits, len);
+ else
+ mask_upper_bits((void *) dst_ptr,
+ chn->format.bits, len);
+ }
}
}
void iio_channel_convert_inverse(const struct iio_channel *chn,
void *dst, const void *src)
{
+ uintptr_t src_ptr = (uintptr_t) src, dst_ptr = (uintptr_t) dst;
unsigned int len = chn->format.length / 8;
+ ptrdiff_t end = len * chn->format.repeat;
+ uintptr_t end_ptr = dst_ptr + end;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
bool swap = chn->format.is_be;
#else
@@ -557,23 +573,26 @@ void iio_channel_convert_inverse(const struct iio_channel *chn,
if (len > sizeof(buf))
return;
- memcpy(buf, src, len);
- mask_upper_bits(buf, chn->format.bits, len);
+ for (dst_ptr = (uintptr_t) dst; dst_ptr < end_ptr;
+ src_ptr += len, dst_ptr += len) {
+ memcpy(buf, (const void *) src_ptr, len);
+ mask_upper_bits(buf, chn->format.bits, len);
- if (chn->format.shift)
- shift_bits(buf, chn->format.shift, len, true);
+ 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);
+ if (len == 1 || !swap)
+ memcpy((void *) dst_ptr, buf, len);
+ else
+ byte_swap((void *) dst_ptr, 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;
+ unsigned int length = chn->format.length / 8 * chn->format.repeat;
uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf);
ptrdiff_t buf_step = iio_buffer_step(buf);
@@ -588,7 +607,7 @@ 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;
+ unsigned int length = chn->format.length / 8 * chn->format.repeat;
uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf);
ptrdiff_t buf_step = iio_buffer_step(buf);
@@ -604,7 +623,7 @@ 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;
+ unsigned int length = chn->format.length / 8 * chn->format.repeat;
uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf);
ptrdiff_t buf_step = iio_buffer_step(buf);
@@ -619,7 +638,7 @@ 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;
+ unsigned int length = chn->format.length / 8 * chn->format.repeat;
uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf);
ptrdiff_t buf_step = iio_buffer_step(buf);
diff --git a/device.c b/device.c
index 75247bbfc..bad9854ec 100644
--- a/device.c
+++ b/device.c
@@ -433,7 +433,8 @@ ssize_t iio_device_get_sample_size_mask(const struct iio_device *dev,
for (i = 0; i < dev->nb_channels; i++) {
const struct iio_channel *chn = dev->channels[i];
- unsigned int length = chn->format.length / 8;
+ unsigned int length = chn->format.length / 8 *
+ chn->format.repeat;
if (chn->index < 0)
break;
diff --git a/examples/Makefile b/examples/Makefile
index 3b27bb351..29699e856 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -14,7 +14,7 @@
# Lesser General Public License for more details.
-TARGETS := ad9361-iiostream iio-monitor
+TARGETS := ad9361-iiostream dummy-iiostream iio-monitor
CFLAGS = -Wall
@@ -28,5 +28,8 @@ iio-monitor: iio-monitor.o
ad9361-iiostream : ad9361-iiostream.o
$(CC) -o $@ $^ $(LDFLAGS) -liio
+dummy-iiostream : dummy-iiostream.o
+ $(CC) -o $@ $^ $(LDFLAGS) -liio
+
clean:
rm -f $(TARGETS) $(TARGETS:%=%.o)
diff --git a/examples/dummy-iiostream.c b/examples/dummy-iiostream.c
new file mode 100644
index 000000000..c43b0e748
--- /dev/null
+++ b/examples/dummy-iiostream.c
@@ -0,0 +1,335 @@
+/*
+ * libiio - Dummy IIO streaming example
+ *
+ * This example libiio program is meant to exercise the features of IIO present
+ * in the sample dummy IIO device. For buffered access it relies on the hrtimer
+ * trigger but could be modified to use the sysfs trigger. No hardware should
+ * be required to run this program.
+ *
+ * How to setup the sample IIO dummy device and hrtimer trigger:
+ *
+ * 1. sudo modprobe industrialio kfifo_buf industrialio-sw-trigger
+ * 2. sudo modprobe iio_dummy iio-trig-hrtimer
+ * 3. sudo mkdir /configfs
+ * 4. sudo mount -t configfs none /config
+ * 5. sudo mkdir /config/iio/triggers/hrtimer/instance1
+ *
+ * Copyright (c) 2016, DAQRI. All rights reserved.
+ * Author: Lucas Magasweran
+ *
+ * Based on AD9361 example:
+ * Copyright (C) 2014 IABG mbH
+ * Author: Michael Feilen
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+static char *name = "iio_dummy_part_no";
+static char *trigger_str = "instance1";
+static int buffer_length = 1;
+static int count = -1;
+
+// libiio supports multiple methods for reading data from a buffer
+enum {
+ BUFFER_POINTER,
+ SAMPLE_CALLBACK,
+ CHANNEL_READ_RAW,
+ CHANNEL_READ,
+ MAX_READ_METHOD,
+};
+static int buffer_read_method = BUFFER_POINTER;
+
+// Streaming devices
+static struct iio_device *dev;
+
+/* IIO structs required for streaming */
+static struct iio_context *ctx;
+static struct iio_buffer *rxbuf;
+static struct iio_channel **channels;
+static int channel_count;
+
+static bool stop;
+static bool has_repeat;
+
+/* cleanup and exit */
+static void shutdown()
+{
+ if (channels) { free(channels); }
+
+ printf("* Destroying buffers\n");
+ if (rxbuf) { iio_buffer_destroy(rxbuf); }
+
+ printf("* Disassociate trigger\n");
+ if (dev) { iio_device_set_trigger(dev, NULL); }
+
+ printf("* Destroying context\n");
+ if (ctx) { iio_context_destroy(ctx); }
+ exit(0);
+}
+
+static void handle_sig(int sig)
+{
+ printf("Waiting for process to finish...\n");
+ stop = true;
+}
+
+static ssize_t sample_cb(const struct iio_channel *chn, void *src, size_t bytes, void *d)
+{
+ const struct iio_data_format *fmt = iio_channel_get_data_format(chn);
+ unsigned int repeat = has_repeat ? fmt->repeat : 1;
+
+ printf("%s ", iio_channel_get_id(chn));
+ for (int j = 0; j < repeat; ++j) {
+ if (bytes == sizeof(int16_t))
+ printf("%i ", ((int16_t *)src)[j]);
+ else if (bytes == sizeof(int64_t))
+ printf("%ld ", ((int64_t *)src)[j]);
+ }
+
+ return bytes * repeat;
+}
+
+static void usage(int argc, char *argv[])
+{
+ printf("Usage: %s [OPTION]\n", argv[0]);
+ printf(" -d\tdevice name (default \"iio_dummy_part_no\")\n");
+ printf(" -t\ttrigger name (default \"instance1\")\n");
+ printf(" -b\tbuffer length (default 1)\n");
+ printf(" -r\tread method (default 0 pointer, 1 callback, 2 read, 3 read raw)\n");
+ printf(" -c\tread count (default no limit)\n");
+}
+
+static void parse_options(int argc, char *argv[])
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "d:t:b:r:c:h")) != -1) {
+ switch (c)
+ {
+ case 'd':
+ name = optarg;
+ break;
+ case 't':
+ trigger_str = optarg;
+ break;
+ case 'b':
+ buffer_length = atoi(optarg);
+ break;
+ case 'r':
+ if (atoi(optarg) >= 0 && atoi(optarg) < MAX_READ_METHOD) {
+ buffer_read_method = atoi(optarg);
+ } else {
+ usage(argc, argv);
+ exit(1);
+ }
+ break;
+ case 'c':
+ if (atoi(optarg) > 0) {
+ count = atoi(optarg);
+ } else {
+ usage(argc, argv);
+ exit(1);
+ }
+ break;
+ case 'h':
+ default:
+ usage(argc, argv);
+ exit(1);
+ }
+ }
+}
+
+/* simple configuration and streaming */
+int main (int argc, char **argv)
+{
+ // Hardware trigger
+ struct iio_device *trigger;
+
+ parse_options(argc, argv);
+
+ // Listen to ctrl+c and assert
+ signal(SIGINT, handle_sig);
+
+ unsigned int major, minor;
+ char git_tag[8];
+ iio_library_get_version(&major, &minor, git_tag);
+ printf("Library version: %u.%u (git tag: %s)\n", major, minor, git_tag);
+
+ /* check for struct iio_data_format.repeat support */
+ has_repeat = major >= 0 && minor >= 8 ? true : false;
+
+ printf("* Acquiring IIO context\n");
+ assert((ctx = iio_create_default_context()) && "No context");
+ assert(iio_context_get_devices_count(ctx) > 0 && "No devices");
+
+ printf("* Acquiring device %s\n", name);
+ dev = iio_context_find_device(ctx, name);
+ if (!dev) {
+ perror("No device found");
+ shutdown();
+ }
+
+ printf("* Initializing IIO streaming channels:\n");
+ for (int i = 0; i < iio_device_get_channels_count(dev); ++i) {
+ struct iio_channel *chn = iio_device_get_channel(dev, i);
+ if (iio_channel_is_scan_element(chn)) {
+ printf("%s\n", iio_channel_get_id(chn));
+ channel_count++;
+ }
+ }
+ if (channel_count == 0) {
+ printf("No scan elements found (make sure the driver built with 'CONFIG_IIO_SIMPLE_DUMMY_BUFFER=y')\n");
+ shutdown();
+ }
+ channels = calloc(channel_count, sizeof *channels);
+ if (!channels) {
+ perror("Channel array allocation failed");
+ shutdown();
+ }
+ for (int i = 0; i < channel_count; ++i) {
+ struct iio_channel *chn = iio_device_get_channel(dev, i);
+ if (iio_channel_is_scan_element(chn))
+ channels[i] = chn;
+ }
+
+ printf("* Acquiring trigger %s\n", trigger_str);
+ trigger = iio_context_find_device(ctx, trigger_str);
+ if (!trigger || !iio_device_is_trigger(trigger)) {
+ perror("No trigger found (try setting up the iio-trig-hrtimer module)");
+ shutdown();
+ }
+
+ printf("* Enabling IIO streaming channels for buffered capture\n");
+ for (int i = 0; i < channel_count; ++i)
+ iio_channel_enable(channels[i]);
+
+ printf("* Enabling IIO buffer trigger\n");
+ if (iio_device_set_trigger(dev, trigger)) {
+ perror("Could not set trigger\n");
+ shutdown();
+ }
+
+ printf("* Creating non-cyclic IIO buffers with %d samples\n", buffer_length);
+ rxbuf = iio_device_create_buffer(dev, buffer_length, false);
+ if (!rxbuf) {
+ perror("Could not create buffer");
+ shutdown();
+ }
+
+ printf("* Starting IO streaming (press CTRL+C to cancel)\n");
+ bool has_ts = strcmp(iio_channel_get_id(channels[channel_count-1]), "timestamp") == 0;
+ int64_t last_ts = 0;
+ while (!stop)
+ {
+ ssize_t nbytes_rx;
+ void *p_dat, *p_end;
+ ptrdiff_t p_inc;
+ int64_t now_ts;
+
+ // Refill RX buffer
+ nbytes_rx = iio_buffer_refill(rxbuf);
+ if (nbytes_rx < 0) {
+ printf("Error refilling buf: %d\n", (int)nbytes_rx);
+ shutdown();
+ }
+
+ p_inc = iio_buffer_step(rxbuf);
+ p_end = iio_buffer_end(rxbuf);
+
+ // Print timestamp delta in ms
+ if (has_ts)
+ for (p_dat = iio_buffer_first(rxbuf, channels[channel_count-1]); p_dat < p_end; p_dat += p_inc) {
+ now_ts = (((int64_t *)p_dat)[0]);
+ printf("[%04ld] ", last_ts > 0 ? (now_ts - last_ts)/1000/1000 : 0);
+ last_ts = now_ts;
+ }
+
+ // Print each captured sample
+ switch (buffer_read_method)
+ {
+ case BUFFER_POINTER:
+ for (int i = 0; i < channel_count; ++i) {
+ const struct iio_data_format *fmt = iio_channel_get_data_format(channels[i]);
+ unsigned int repeat = has_repeat ? fmt->repeat : 1;
+
+ printf("%s ", iio_channel_get_id(channels[i]));
+ for (p_dat = iio_buffer_first(rxbuf, channels[i]); p_dat < p_end; p_dat += p_inc) {
+ for (int j = 0; j < repeat; ++j) {
+ if (fmt->length/8 == sizeof(int16_t))
+ printf("%i ", ((int16_t *)p_dat)[j]);
+ else if (fmt->length/8 == sizeof(int64_t))
+ printf("%ld ", ((int64_t *)p_dat)[j]);
+ }
+ }
+ }
+ printf("\n");
+ break;
+
+ case SAMPLE_CALLBACK:
+ iio_buffer_foreach_sample(rxbuf, sample_cb, NULL);
+ printf("\n");
+ break;
+
+ case CHANNEL_READ_RAW:
+ case CHANNEL_READ:
+ for (int i = 0; i < channel_count; ++i) {
+ uint8_t *buf;
+ size_t bytes;
+ const struct iio_data_format *fmt = iio_channel_get_data_format(channels[i]);
+ unsigned int repeat = has_repeat ? fmt->repeat : 1;
+ size_t sample_size = fmt->length / 8 * repeat;
+
+ buf = malloc(sample_size * buffer_length);
+
+ if (buffer_read_method == CHANNEL_READ_RAW)
+ bytes = iio_channel_read_raw(channels[i], rxbuf, buf, sample_size * buffer_length);
+ else
+ bytes = iio_channel_read(channels[i], rxbuf, buf, sample_size * buffer_length);
+
+ printf("%s ", iio_channel_get_id(channels[i]));
+ for (int sample = 0; sample < bytes / sample_size; ++sample) {
+ for (int j = 0; j < repeat; ++j) {
+ if (fmt->length / 8 == sizeof(int16_t))
+ printf("%i ", ((int16_t *)buf)[sample+j]);
+ else if (fmt->length / 8 == sizeof(int64_t))
+ printf("%li ", ((int64_t *)buf)[sample+j]);
+ }
+ }
+
+ free(buf);
+ }
+ printf("\n");
+ break;
+ }
+
+ if (count != -1 && --count == 0)
+ break;
+ }
+
+ shutdown();
+
+ return 0;
+}
+
diff --git a/iio.h b/iio.h
index 7100d955e..151798864 100644
--- a/iio.h
+++ b/iio.h
@@ -1174,6 +1174,7 @@ __api void * iio_buffer_end(const struct iio_buffer *buf);
* @param buf A pointer to an iio_buffer structure
* @param callback A pointer to a function to call for each sample found
* @param data A user-specified pointer that will be passed to the callback
+ * @return number of bytes processed.
*
* NOTE: The callback receives four arguments:
* * A pointer to the iio_channel structure corresponding to the sample,
@@ -1232,6 +1233,9 @@ struct iio_data_format {
/** @brief Contains the scale to apply if with_scale is set */
double scale;
+
+ /** @brief Number of times length repeats (added in v0.8) */
+ unsigned int repeat;
};
diff --git a/local.c b/local.c
index 4d9559211..9b18644bb 100644
--- a/local.c
+++ b/local.c
@@ -1108,9 +1108,16 @@ static int handle_protected_scan_element_attr(struct iio_channel *chn,
if (ret > 0) {
char endian, sign;
- sscanf(buf, "%ce:%c%u/%u>>%u", &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'||
diff --git a/mainpage.dox b/mainpage.dox
index d3297ebd0..dd312312d 100644
--- a/mainpage.dox
+++ b/mainpage.dox
@@ -247,4 +247,10 @@ Those debug parameters can be written using the following functions:
As for debug attributes, some IIO devices also offer the possibility to read and write hardware registers directly.
In libiio, this can be done with two functions, iio_device_reg_read() and iio_device_reg_write().
+@section abi Application Binary Interface
+
+The libiio ABI tries to be both backwards and forwards compatible.
+This means applications compiled against an older version will work fine with a newer dynamically linked library. Applications compiled against a newer version will work fine with an older dynamically linked library so long as they don't access any new features. Applications using new features should ensure the libiio version is compatible by using iio_library_get_version() to avoid undefined behavior.
+
+
*/
diff --git a/tests/iio_info.c b/tests/iio_info.c
index bfd508801..31dddbc5c 100644
--- a/tests/iio_info.c
+++ b/tests/iio_info.c
@@ -244,15 +244,21 @@ int main(int argc, char **argv)
const struct iio_data_format *format =
iio_channel_get_data_format(ch);
char sign = format->is_signed ? 's' : 'u';
+ char repeat[8] = "";
if (format->is_fully_defined)
sign += 'A' - 'a';
- printf(", index: %lu, format: %ce:%c%u/%u>>%u)\n",
- iio_channel_get_index(ch),
- format->is_be ? 'b' : 'l',
- sign, format->bits,
- format->length, format->shift);
+ if (format->repeat > 1)
+ snprintf(repeat, sizeof(repeat), "X%u",
+ format->repeat);
+
+ printf(", index: %lu, format: %ce:%c%u/%u%s>>%u)\n",
+ iio_channel_get_index(ch),
+ format->is_be ? 'b' : 'l',
+ sign, format->bits,
+ format->length, repeat,
+ format->shift);
} else {
printf(")\n");
}
diff --git a/xml.c b/xml.c
index f06af7e14..fbb6d246c 100644
--- a/xml.c
+++ b/xml.c
@@ -123,10 +123,19 @@ static void setup_scan_element(struct iio_channel *chn, xmlNode *n)
chn->index = atol(content);
} else if (!strcmp(name, "format")) {
char e, s;
- sscanf(content, "%ce:%c%u/%u>>%u", &e, &s,
+ if (strchr(content, 'X')) {
+ sscanf(content, "%ce:%c%u/%uX%u>>%u", &e, &s,
&chn->format.bits,
&chn->format.length,
+ &chn->format.repeat,
&chn->format.shift);
+ } else {
+ chn->format.repeat = 1;
+ sscanf(content, "%ce:%c%u/%u>>%u", &e, &s,
+ &chn->format.bits,
+ &chn->format.length,
+ &chn->format.shift);
+ }
chn->format.is_be = e == 'b';
chn->format.is_signed = (s == 's' || s == 'S');
chn->format.is_fully_defined = (s == 'S' || s == 'U' ||