libiio: dummy-iiostream.c

libiio 1.0 Library for interfacing with IIO devices

Loading...
Searching...
No Matches
dummy-iiostream.c

This example libiio program is meant to exercise the features of IIO present in the sample dummy IIO device in the linux kernel.

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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.
*
* Copyright (c) 2016, DAQRI. All rights reserved.
* Author: Lucas Magasweran <lucas.magasweran@daqri.com>
*
* Based on AD9361 example:
* Copyright (C) 2014 IABG mbH
* Author: Michael Feilen <feilen_at_iabg.de>
*/
/*
* How to setup the sample IIO dummy device and hrtimer trigger:
*
* 1. Check if `configfs` is already mounted
*
* $ mount | grep 'config'
* configfs on /sys/kernel/config type configfs (rw,relatime)
*
* 1.b. Mount `configfs` if it is not already mounted
* $ sudo mount -t configfs none /sys/kernel/config
*
* 2. Load modules one by one
*
* $ sudo modprobe industrialio
* $ sudo modprobe industrialio-configfs
* $ sudo modprobe industrialio-sw-device
* $ sudo modprobe industrialio-sw-trigger
* $ sudo modprobe iio-trig-hrtimer
* $ sudo modprobe iio_dummy
*
* 3. Create trigger and dummy device under `/sys/kernel/config`
*
* $ sudo mkdir /sys/kernel/config/iio/triggers/hrtimer/instance1
* $ sudo mkdir /sys/kernel/config/iio/devices/dummy/my_dummy_device
*
* 4. Run `iio_info` to see that all worked properly
*
* $ iio_info
* Library version: 0.14 (git tag: c9909f2)
* Compiled with backends: local xml ip
* IIO context created with local backend.
* Backend version: 0.14 (git tag: c9909f2)
* Backend description string: Linux ...
* IIO context has 1 attributes:
* local,kernel: 4.13.0-39-generic
* IIO context has 2 devices:
* iio:device0: my_dummy_device
* 10 channels found:
* activity_walking: (input)
* 1 channel-specific attributes found:
* attr 0: input value: 4
* ...
*
**/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include <iio/iio.h>
#include <iio/iio-debug.h>
#define IIO_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define IIO_ENSURE(expr) { \
if (!(expr)) { \
(void) fprintf(stderr, "assertion failed (%s:%d)\n", __FILE__, __LINE__); \
(void) abort(); \
} \
}
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 unsigned int channel_count;
static struct iio_channels_mask *rxmask;
static struct iio_stream *rxstream;
static bool stop;
static bool has_repeat;
/* cleanup resources */
static void cleanup(void)
{
int ret;
if (channels) { free(channels); }
printf("* Destroying stream\n");
if (rxstream) { iio_stream_destroy(rxstream); }
printf("* Disassociate trigger\n");
if (dev) {
ret = iio_device_set_trigger(dev, NULL);
if (ret < 0) {
char buf[256];
iio_strerror(-ret, buf, sizeof(buf));
fprintf(stderr, "%s while Disassociate trigger\n", buf);
}
}
printf("* Destroying channel masks\n");
if (rxmask)
iio_channels_mask_destroy(rxmask);
printf("* Destroying context\n");
if (ctx) { iio_context_destroy(ctx); }
}
/* cleanup and exit with error */
static void shutdown(void)
{
cleanup();
exit(1);
}
static void handle_sig(int sig)
{
printf("Waiting for process to finish... got signal : %d\n", sig);
stop = true;
}
static ssize_t sample_cb(const struct iio_channel *chn, void *src, size_t bytes, __notused void *d)
{
const struct iio_data_format *fmt = iio_channel_get_data_format(chn);
unsigned int j, repeat = has_repeat ? fmt->repeat : 1;
printf("%s ", iio_channel_get_id(chn));
for (j = 0; j < repeat; ++j) {
if (bytes == sizeof(int16_t))
printf("%" PRIi16 " ", ((int16_t *)src)[j]);
else if (bytes == sizeof(int64_t))
printf("%" PRId64 " ", ((int64_t *)src)[j]);
}
return bytes * repeat;
}
static void usage(__notused 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 raw, 3 read)\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);
shutdown();
}
break;
case 'c':
if (atoi(optarg) > 0) {
count = atoi(optarg);
} else {
usage(argc, argv);
shutdown();
}
break;
case 'h':
default:
usage(argc, argv);
shutdown();
}
}
}
/* simple configuration and streaming */
int main (int argc, char **argv)
{
unsigned int i, j,
major = iio_context_get_version_major(NULL),
minor = iio_context_get_version_minor(NULL);
int err;
// Hardware trigger
struct iio_device *trigger;
parse_options(argc, argv);
// Listen to ctrl+c and assert
signal(SIGINT, handle_sig);
printf("Library version: %u.%u (git tag: %s)\n", major, minor,
iio_context_get_version_tag(NULL));
/* check for struct iio_data_format.repeat support
* 0.8 has repeat support, so anything greater than that */
has_repeat = ((major * 10000) + minor) >= 8 ? true : false;
printf("* Acquiring IIO context\n");
ctx = iio_create_context(NULL, NULL);
err = iio_err(ctx);
IIO_ENSURE(!err && "No context");
IIO_ENSURE(iio_context_get_devices_count(ctx) > 0 && "No devices");
printf("* Acquiring device %s\n", name);
dev = iio_context_find_device(ctx, name);
if (!dev) {
fprintf(stderr, "No device found");
shutdown();
}
printf("* Initializing IIO streaming channels:\n");
rxmask = iio_create_channels_mask(iio_device_get_channels_count(dev));
if (!rxmask) {
fprintf(stderr, "Unable to allocate channels mask\n");
shutdown();
}
for (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) {
fprintf(stderr, "Channel array allocation failed");
shutdown();
}
for (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)) {
fprintf(stderr, "No trigger found (try setting up the iio-trig-hrtimer module)");
shutdown();
}
printf("* Enabling IIO streaming channels for buffered capture\n");
for (i = 0; i < channel_count; ++i)
iio_channel_enable(channels[i], rxmask);
printf("* Enabling IIO buffer trigger\n");
if (iio_device_set_trigger(dev, trigger)) {
fprintf(stderr, "Could not set trigger\n");
shutdown();
}
printf("* Creating non-cyclic RX IIO buffer\n");
rxbuf = iio_device_get_buffer(dev, 0);
if (!rxbuf) {
ctx_perror(ctx, -ENODEV, "Could not get buffer");
shutdown();
}
printf("* Creating IIO blocks of %u bytes\n", buffer_length);
rxstream = iio_buffer_create_stream(rxbuf, 4, buffer_length, rxmask);
if (iio_err(rxstream)) {
rxstream = NULL;
ctx_perror(ctx, iio_err(rxstream), "Could not create RX stream");
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)
{
const struct iio_block *rxblock;
/* we use a char pointer, rather than a void pointer, for p_dat & p_end
* to ensure the compiler understands the size is a byte, and then we
* can do math on it.
*/
char *p_dat, *p_end;
ptrdiff_t p_inc;
int64_t now_ts;
rxblock = iio_stream_get_next_block(rxstream);
err = iio_err(rxblock);
if (err) {
ctx_perror(ctx, err, "Unable to receive block");
shutdown();
}
p_inc = iio_device_get_sample_size(dev, rxmask);
p_end = iio_block_end(rxblock);
// Print timestamp delta in ms
if (has_ts)
for (p_dat = iio_block_first(rxblock, channels[channel_count-1]); p_dat < p_end; p_dat += p_inc) {
now_ts = (((int64_t *)p_dat)[0]);
printf("[%04" PRId64 "] ", 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 (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_block_first(rxblock, channels[i]); p_dat < p_end; p_dat += p_inc) {
for (j = 0; j < repeat; ++j) {
if (fmt->length/8 == sizeof(int16_t))
printf("%" PRIi16 " ", ((int16_t *)p_dat)[j]);
else if (fmt->length/8 == sizeof(int64_t))
printf("%" PRId64 " ", ((int64_t *)p_dat)[j]);
}
}
}
printf("\n");
break;
case SAMPLE_CALLBACK: {
ssize_t ret;
ret = iio_block_foreach_sample(rxblock, rxmask, sample_cb, NULL);
if (ret < 0)
dev_perror(dev, (int)ret, "Unable to process buffer");
printf("\n");
break;
}
case CHANNEL_READ_RAW:
case CHANNEL_READ:
for (i = 0; i < channel_count; ++i) {
uint8_t *buf;
size_t sample, 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 (!buf) {
perror("trying to allocate memory for buffer\n");
shutdown();
}
bytes = iio_channel_read(channels[i], rxblock, buf,
sample_size * buffer_length,
buffer_read_method == CHANNEL_READ_RAW);
printf("%s ", iio_channel_get_id(channels[i]));
for (sample = 0; sample < bytes / sample_size; ++sample) {
for (j = 0; j < repeat; ++j) {
if (fmt->length / 8 == sizeof(int16_t))
printf("%" PRIi16 " ", ((int16_t *)buf)[sample+j]);
else if (fmt->length / 8 == sizeof(int64_t))
printf("%" PRId64 " ", ((int64_t *)buf)[sample+j]);
}
}
free(buf);
}
printf("\n");
break;
}
if (count != -1 && --count == 0)
break;
}
cleanup();
return 0;
}
iio_block_first
void * iio_block_first(const struct iio_block *block, const struct iio_channel *chn)
Find the first sample of a channel in a block.
Definition block.c:208
iio_block_foreach_sample
__api __check_ret ssize_t iio_block_foreach_sample(const struct iio_block *block, const struct iio_channels_mask *mask, ssize_t(*callback)(const struct iio_channel *chn, void *src, size_t bytes, void *d), void *data)
Call the supplied callback for each sample found in a block.
iio_block_end
void * iio_block_end(const struct iio_block *block)
Get the address after the last sample in a block.
Definition block.c:203
iio_data_format::repeat
unsigned int repeat
Number of times length repeats (added in v0.8)
Definition iio.h:1583
iio_data_format::length
unsigned int length
Total length of the sample, in bits.
Definition iio.h:1559
iio_channel_is_scan_element
bool iio_channel_is_scan_element(const struct iio_channel *chn)
Return True if the given channel is a scan element.
Definition channel.c:365
iio_channel_read
size_t iio_channel_read(const struct iio_channel *chn, const struct iio_block *block, void *dst, size_t len, bool raw)
Demultiplex and convert the samples of a given channel.
Definition channel.c:627
iio_channel_enable
void iio_channel_enable(const struct iio_channel *chn, struct iio_channels_mask *mask)
Enable the given channel.
Definition channel.c:444
iio_channel_get_id
const char * iio_channel_get_id(const struct iio_channel *chn)
Retrieve the channel ID (e.g. voltage0)
Definition channel.c:345
iio_context_find_device
struct iio_device * iio_context_find_device(const struct iio_context *ctx, const char *name)
Try to find a device structure by its ID, label or name.
Definition compat.c:710
iio_create_context
struct iio_context * iio_create_context(const struct iio_context_params *params, const char *uri)
Create a context from a URI description.
Definition context.c:716
iio_context_destroy
void iio_context_destroy(struct iio_context *ctx)
Destroy the given context.
Definition compat.c:434
iio_context_get_version_minor
unsigned int iio_context_get_version_minor(const struct iio_context *ctx)
Get the minor number of the library version.
Definition context.c:626
iio_context_get_version_major
unsigned int iio_context_get_version_major(const struct iio_context *ctx)
Get the major number of the library version.
Definition context.c:618
iio_context_get_version_tag
const char * iio_context_get_version_tag(const struct iio_context *ctx)
Get the git hash string of the library version.
Definition context.c:634
iio_context_get_devices_count
unsigned int iio_context_get_devices_count(const struct iio_context *ctx)
Enumerate the devices found in the given context.
Definition compat.c:698
iio_create_channels_mask
struct iio_channels_mask * iio_create_channels_mask(unsigned int nb_channels)
Create a new empty channels mask.
Definition mask.c:14
iio_channels_mask_destroy
void iio_channels_mask_destroy(struct iio_channels_mask *mask)
Destroy a channels mask.
Definition mask.c:40
iio_channel_get_data_format
const struct iio_data_format * iio_channel_get_data_format(const struct iio_channel *chn)
Get a pointer to a channel's data format structure.
Definition channel.c:432
iio_device_get_buffer
struct iio_buffer * iio_device_get_buffer(const struct iio_device *dev, unsigned int index)
Get the buffer present at the given index.
Definition device.c:254
iio_device_set_trigger
int iio_device_set_trigger(const struct iio_device *dev, const struct iio_device *trigger)
Associate a trigger to a given device.
Definition compat.c:1606
iio_device_is_trigger
bool iio_device_is_trigger(const struct iio_device *dev)
Return True if the given device is a trigger.
Definition compat.c:1613
iio_device_get_channel
struct iio_channel * iio_device_get_channel(const struct iio_device *dev, unsigned int index)
Get the channel present at the given index.
Definition compat.c:963
iio_device_get_channels_count
unsigned int iio_device_get_channels_count(const struct iio_device *dev)
Enumerate the channels of the given device.
Definition compat.c:857
iio_stream_destroy
void iio_stream_destroy(struct iio_stream *stream)
Destroy the given stream object.
Definition stream.c:86
iio_buffer_create_stream
struct iio_stream * iio_buffer_create_stream(struct iio_buffer *buffer, size_t nb_blocks, size_t samples_count, struct iio_channels_mask *mask)
Create a iio_stream object for the given iio_buffer.
Definition stream.c:26
iio_stream_get_next_block
const struct iio_block * iio_stream_get_next_block(struct iio_stream *stream)
Get a pointer to the next data block.
Definition stream.c:110
iio_strerror
void iio_strerror(int err, char *dst, size_t len)
Get a string description of an error code.
Definition compat.c:1991
iio.h
Public interface.
iio_buffer
An input or output buffer, used to read or write samples.
iio_channel
Represents an input or output channel of a device.
iio_context
Contains the representation of an IIO context.
iio_data_format
Contains the format of a data sample.
Definition iio.h:1557
iio_device
Represents a device in the IIO context.