-
Notifications
You must be signed in to change notification settings - Fork 318
libiio_0_to_1
The new Libiio 1.x breaks the ABI from the v0.25 version. This means that a program compiled against libiio v0.25 will not run against Libiio 1.x.
This is because the new library introduced new functions, removed some others, or changed the prototype of some functions. Therefore, to make an application compatible with Libiio 1.x, some changes are needed.
This document aims to list all the changes that are needed for an application designed for Libiio 0.25 to compile and run against Libiio 1.x.
The iio.h
public header has been moved to a iio
subdirectory. Therefore you
now have to include <iio/iio.h>
instead of <iio.h>
.
Alongside this file are some new public headers:
-
<iio/iio-debug.h>
contains macros that can be used to print messages to the standard or error output paths. -
<iio/iio-lock.h>
provides OS-independent mutex and thread routines. -
<iio/iio-backend.h>
provides structure definitions and symbols that can be used to develop a plug-in backend for Libiio. -
<iio/iiod-client.h>
provides all the definitions needed for a plug-in backend to interface with the IIOD server.
Starting from Libiio v1.0, most API functions that return a pointer won't return
NULL on error. Instead, they will return an error code encoded into the pointer
value. The documentation embedded in iio.h
will specify if a given function
returns a pointer-encoded error.
You can get the error code from the pointer using the iio_err
function.
If it returns zero, then the pointer is valid and can be used directly.
Note that the error code, if set, will always be negative. You can get a textual
representation of the error using iio_strerror
, or use the log macros
prm_perror
, ctx_perror
, dev_perror
or chn_perror
from
<iio/iio-debug.h>
.
The errno
variable is not used anymore to return error codes, so it is now an
error to check it after a libiio call. With that said, it is not guaranteed that
libiio won't modify this variable, as system calls may modify it.
Libiio 1.x introduces the notion of context parameters. These are contained in
the publicly visible iio_context_params
structure. The following parameters
can be set:
-
timeout_ms
: the delay in milliseconds after which input/output operations (streaming samples, reading attributes, IIOD protocol commands etc.) will throw a timed-out error. If set to zero, the backend's default timeout value is used.Note that the timeout value can be changed after the context is created, using the
iio_context_set_timeout
function. -
out
anderr
: output paths for the library's debug and information messages (forout
) and warning and error messages (forerr
). If set to NULL, these will default tostdout
andstderr
respectively. -
log_level
: allow configuring the verbosity of Libiio's output messages. If zero, it will default to the log level configured at compilation time, which is generallyLEVEL_INFO
. Only the messages whose level is lower or equal than this threshold will be displayed. -
stderr_level
: Messages whose level is lower or equal than this threshold are sent to the error output path; messages whose level is higher are sent to the regular output path. If zero, it will default toLEVEL_WARNING
.Note that it is possible to send all messages to the standard output path by setting this parameter to
LEVEL_NOLOG
, and also to send all messages to the error output by setting this parameter toLEVEL_DEBUG
.
Libiio context creation is now handled uniquely by the iio_create_context
function. The first parameter is a pointer to a iio_context_params
structure;
it is completely optional, and NULL is accepted. The second parameter
is the URI that identifies the IIO context. The function will now return
pointer-encoded codes en errors, so you must use iio_err
to verify that the
call succeeded.
Furthermore:
-
iio_create_context_from_uri
is replaced byiio_create_context
. The same URI argument can be passed as-is. -
iio_create_local_context
is gone. You can still create a local context by using the URIlocal:
. -
iio_create_default_context
is gone. You can still create the default context by passing a NULL pointer as the URI parameter. -
iio_create_network_context
is gone. You can create a network context by using theip:
URI prefix followed by a hostname, IPv4 or IPv6 address. -
iio_create_xml_context
is gone. You can create a XML context by using thexml:
URI prefix followed by the path to the XML file on disk. -
iio_create_xml_context_mem
is gone. You can create a XML context from memory by using thexml:
URI prefix followed by the NULL-terminated XML string. -
iio_context_clone
is gone. You can however clone a context by retrieving the params withiio_context_get_params
(or just use NULL if that's what you used for the first context), and the URI withiio_context_get_attr_value(ctx, "uri")
, and just creating a new context with these two parameters.
The API provided by libiio to discover remote libiio contexts has changed
completely. Two different yet similar APIs were provided in libiio v0.25
(the one based on iio_scan_context
and the one based on iio_scan_block
)
which was redundant and very confusing.
In libiio 1.0, there is a single scan API based on the iio_scan
object.
The function iio_scan
will create a scan context with the given backends. The
backends string uses the same format as in v0.25, but use commas instead of
colons as the delimiter.
Using the newly created object, you can obtain the number of libiio contexts
found using iio_scan_get_results_count
, and get each entry's description
string and URI using iio_scan_get_description
and iio_scan_get_uri
respectively, passing as argument the index of the context you're interested in.
Finally, you can use iio_scan_destroy
to free the iio_scan
object.
The various IIO objects still have attributes. However, the API changed
slightly.
In Libiio v0.25, iio_device_attr_read
, iio_device_debug_attr_read
,
iio_device_buffer_attr_read
and iio_channel_attr_read
could be used to read
raw bytes or a string into a buffer. This functionality is now provided by
iio_device_attr_read_raw
, iio_device_debug_attr_read_raw
,
iio_device_buffer_attr_read_raw
and iio_channel_attr_read_raw
respectively.
The old names are now generic (as in C11 _Generic) macros, which means that the
ptr
argument can point to different types: bool
, long long
and double
.
With that said, the specific functions of v0.25 to read these types are still
present in Libiio v1.0; the generic macro is only present for convenience, and
is not mandatory, which means that it is still possible to use Libiio with
C89 or C99 projects.
The exact same can be said for iio_device_attr_write
,
iio_device_debug_attr_write
, iio_device_buffer_attr_write
and
iio_channel_attr_write
, which are now renamed to
iio_device_attr_write_string
, iio_device_debug_attr_write_string
,
iio_device_buffer_attr_write_string
and iio_channel_attr_write_string
,
respectively. The old names are now generic (C11 _Generic) macros whose val
argument can be
of the following types: bool
, long long
, double
, char *
and
const char *
.
The iio_device_attr_write_raw
, iio_device_debug_attr_write_raw
,
iio_device_debug_attr_write_raw
and iio_channel_attr_write_raw
have not been
modified and their prototype is the same as before.
Starting from Libiio 1.0, the channel state (enabled or disabled) is no longer
an intrinsic property. Instead, a iio_channels_mask
object can be used to
store the state of each channel of a given device. This mask object can then
be passed to various API functions.
The mask object can be created with the function iio_create_channels_mask
,
and destroyed with iio_channels_mask_destroy
.
The iio_channel_enable
and iio_channel_disable
functions will now take a
pointer to a iio_channels_mask
as argument. The channels must be children of
the device associated with the mask object.
The iio_buffer
object changed greatly since Libiio v0.25, and its API is now
very different.
Creating a buffer object still happens with iio_device_create_buffer
,
however the prototype of the function changed. Alongside the iio_device
pointer argument, it will now take as argument the hardware index of the buffer
(which is most likely 0) and a pointer to a iio_channels_mask
associated with
the same iio_device
(failing to comply to this rule will result in undefined
behaviour).
The buffer object gained a few API functions:
-
iio_buffer_get_channels_mask
return a pointer to the internal mask object. It does not point to the mask object passed as argument to the create function. The mask returned may contain more enabled channels than what was requested, and corresponds to the list of channels for which the buffer will deliver samples. -
iio_buffer_enable
starts the streaming process. With the Libiio v0.25 API, this was automatically done internally during the first call toiio_buffer_refill
oriio_buffer_push
, but needs to be done manually now (unless you use theiio_stream
API - keep reading). -
iio_buffer_disable
stops the streaming process.
The big change since Libiio v0.25 is that the buffer object does not provide API functions related to data streaming, or functions to access the data.
Therefore, these functions have been removed:
-
iio_buffer_refill
,iio_buffer_push
andiio_buffer_push_partial
-
iio_buffer_get_poll_fd
andiio_buffer_set_blocking_mode
-
iio_buffer_start
,iio_buffer_end
,iio_buffer_first
,iio_buffer_step
-
iio_buffer_foreach_sample
.
The iio_buffer_cancel
function still exists and its prototype is the same.
The streaming process changed completely in Libiio v1.0, although it is
possible to emulate the old API on top of the new low-level iio_block
API.
As its name suggests, the base object for this new API is called iio_block
.
A block object can be created with iio_buffer_create_block
. The only two
arguments needed are a pointer to the iio_buffer
, and the size (in bytes)
of the block. A block can later be destroyed with iio_block_destroy
.
The iio_block
object contains a "block" of samples (which we won't be calling
a "buffer" to avoid confusion with IIO's buffer objects). The samples can be
accessed using the following functions:
-
iio_block_start
,iio_block_end
to get the block's boundaries; -
iio_block_first
to get a pointer to a channel's first sample in the block; -
iio_block_foreach_sample
to iterate over the samples contained in the block.
Note that there is no "iio_block_step" function, since the step size is basically the buffer's sample size. You can retrieve the step size doing:
const struct iio_channels_mask *mask = iio_buffer_get_channels_mask(buffer);
size_t sample_size = iio_device_get_sample_size(dev, mask);
You can also use the same mask that was used to create the iio_buffer, if
you know that the hardware will never report a different mask than what was
requested. In doubt, retrieving the iio_buffer's mask with
iio_buffer_get_channels_mask
is the safest option.
Instead of a push / refill mechanism, the new iio_block
API works with a queue
mechanism.
-
To push samples to the hardware, fill a block with the samples using the functions described above, then call
iio_block_enqueue
. You can enqueue as many blocks as you have available. -
iio_block_enqueue
is always a non-blocking operation. Once a block has been enqueued, it should be considered owned by the hardware, and therefore it is forbidden to access or modify the underlying memory. It is possible to reuse a block only after it's been successfully dequeued usingiio_block_dequeue
. -
Don't forget to call
iio_buffer_start
to start the streaming process, either before or after enqueueing the first blocks. -
iio_block_dequeue
is a blocking operation, unless thenonblock
parameter is set to true; in which case-EBUSY
will be returned if the block is not yet ready to be dequeued. -
All blocks are in the "dequeued" state after creation. To refill samples from the hardware, you first need to "give back" all the blocks to the hardware, using
iio_block_enqueue
with a transfer length of 0 bytes. The same blocks, after dequeued, will contain the samples received from the hardware.
Note that even though you can enqueue / dequeue the blocks in any order you want, the blocks will be filled by the hardware in the order at which they were enqueued.
Trying to enqueue an already enqueued block or dequeue an already dequeued
block is an invalid operation, and will result in a -EPERM
error being
returned.
Additionally to the queue mechanism described above, a different, simpler API
has been introduced, based on a new iio_stream
object.
This different API should be considered optional and is implemented on top of the queue mechanism; its purpose is only to simplify data streaming for simple applications.
A iio_stream
object can be created with iio_buffer_create_stream
. It takes
three parameters: a pointer to the iio_buffer
object, the number of blocks
to create (a good default is 4), and the size in samples of each block.
This object can later be destroyed with iio_stream_destroy
.
Finally, data streaming (be it for pushing or receiving samples) is done with
a single function, iio_stream_get_next_block
, which will return a pointer to
a iio_block
object.
This iio_block
can then be read from (if receiving samples), or written to
(if uploading samples). To receive new samples or request a new block for
uploading samples, simply call iio_stream_get_next_block
again.
Note that when using the iio_stream
API, it is invalid to call
iio_block_enqueue
, iio_block_dequeue
, iio_buffer_enable
or
iio_buffer_disable
.