diff --git a/doc/doxygen/doxyfile.in b/doc/doxygen/doxyfile.in new file mode 100644 index 000000000..0fe1096f1 --- /dev/null +++ b/doc/doxygen/doxyfile.in @@ -0,0 +1,21 @@ +GENERATE_XML = YES +INPUT = ../../include/iio +OUTPUT_DIRECTORY = generated + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +PREDEFINED = "__api= " \ + "__check_ret= " \ + "__pure= " \ + "__cnst= " + +GENERATE_HTML = NO +GENERATE_LATEX = NO + +EXCLUDE = ../../iio-private.h \ + ../../debug.h \ + ../../iio-lock.h \ + ../../iiod-client.h \ + ../../sort.h \ + ../../network.h diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 000000000..747ffb7b3 --- /dev/null +++ b/doc/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/doc/man_to_rst.py b/doc/man_to_rst.py new file mode 100644 index 000000000..b353a9116 --- /dev/null +++ b/doc/man_to_rst.py @@ -0,0 +1,58 @@ +# Convert man pages to reStructuredText +import os + +manfolder = os.path.join(os.path.dirname(__file__), "..","build", "share","man") +page = ['iio_attr','iio_info','iio_genxml','iio_rwdev','iio_reg','iio_stresstest'] + +tools = {} + +for po in page: + print(f"Converting {po} to reStructuredText") + p = po + ".1" + fullpath = os.path.join(manfolder, p) + target = os.path.join("source", "tools", po + ".rst") + os.system(f"pandoc --from man --to rst {fullpath} -o {target}") + + # Add title to rst file + with open(target, 'r') as file: + data = file.readlines() + + # Downgrade all headers by one level + for i in range(len(data)): + if data[i].startswith("="): + data[i] = data[i].replace("=", "-") + if data[i].startswith("#"): + data[i] = data[i].replace("#", "=") + + data.insert(0, f"{po}\n") + data.insert(1, "=" * len(po) + "\n") + + # Remove NAME line and the following line + for i in range(len(data)): + if data[i].startswith("NAME"): + data.pop(i) + data.pop(i) + ref = data[i+1] + print(f"Reference: {ref}") + tool = ref.split("-")[0].strip() + description = ref.split("-")[1].strip() + tools[tool] = description + break + + with open(target, 'w') as file: + file.writelines(data) + +# Build index for cli tools +print("Building index for cli tools") + +index = os.path.join("source", "tools", "index.rst") + +with open(index, 'w') as file: + file.write("Command Line Tools\n") + file.write("==================\n\n") + file.write(".. toctree::\n") + file.write(" :maxdepth: 1\n\n") + for tool in tools: + file.write(f" {tool}\n") + file.write("\n\n") + diff --git a/doc/requirements_doc.txt b/doc/requirements_doc.txt new file mode 100644 index 000000000..f351245d0 --- /dev/null +++ b/doc/requirements_doc.txt @@ -0,0 +1,7 @@ +sphinx +https://github.com/analogdevicesinc/doctools/releases/download/latest/adi-doctools.tar.gz +sphinxcontrib.wavedrom +myst-parser +breathe +sphinx-inline-tabs +sphinxcontrib-matlabdomain \ No newline at end of file diff --git a/doc/source/_static/codemodel.svg b/doc/source/_static/codemodel.svg new file mode 100644 index 000000000..929fcd666 --- /dev/null +++ b/doc/source/_static/codemodel.svg @@ -0,0 +1,579 @@ + + + +image/svg+xmlattribute +attribute +debugattribute +attribute +attribute +kernel iio drivers +iiod + \ No newline at end of file diff --git a/doc/source/_static/libiio_commands.png b/doc/source/_static/libiio_commands.png new file mode 100644 index 000000000..830f7f597 Binary files /dev/null and b/doc/source/_static/libiio_commands.png differ diff --git a/doc/source/_static/libiio_details_readbuf.png b/doc/source/_static/libiio_details_readbuf.png new file mode 100644 index 000000000..9fbf6a753 Binary files /dev/null and b/doc/source/_static/libiio_details_readbuf.png differ diff --git a/doc/source/_static/libiio_farview.png b/doc/source/_static/libiio_farview.png new file mode 100644 index 000000000..af8574300 Binary files /dev/null and b/doc/source/_static/libiio_farview.png differ diff --git a/doc/source/_static/libiio_highspeed_interface.png b/doc/source/_static/libiio_highspeed_interface.png new file mode 100644 index 000000000..f9a7ded9c Binary files /dev/null and b/doc/source/_static/libiio_highspeed_interface.png differ diff --git a/doc/source/_static/libiio_lowspeed_interface.png b/doc/source/_static/libiio_lowspeed_interface.png new file mode 100644 index 000000000..216cc9599 Binary files /dev/null and b/doc/source/_static/libiio_lowspeed_interface.png differ diff --git a/doc/source/_static/libiio_open_command.png b/doc/source/_static/libiio_open_command.png new file mode 100644 index 000000000..3b4ae03f7 Binary files /dev/null and b/doc/source/_static/libiio_open_command.png differ diff --git a/doc/source/_static/libiio_readbuf_command.png b/doc/source/_static/libiio_readbuf_command.png new file mode 100644 index 000000000..f1be440c9 Binary files /dev/null and b/doc/source/_static/libiio_readbuf_command.png differ diff --git a/doc/source/_static/libiio_route_highspeed.png b/doc/source/_static/libiio_route_highspeed.png new file mode 100644 index 000000000..36b21c307 Binary files /dev/null and b/doc/source/_static/libiio_route_highspeed.png differ diff --git a/doc/source/_static/libiio_route_lowspeed.png b/doc/source/_static/libiio_route_lowspeed.png new file mode 100644 index 000000000..2b16a083c Binary files /dev/null and b/doc/source/_static/libiio_route_lowspeed.png differ diff --git a/doc/source/_static/libiio_route_zerocopy.png b/doc/source/_static/libiio_route_zerocopy.png new file mode 100644 index 000000000..a8a19ee19 Binary files /dev/null and b/doc/source/_static/libiio_route_zerocopy.png differ diff --git a/doc/source/api.rst b/doc/source/api.rst new file mode 100644 index 000000000..b30ba1f36 --- /dev/null +++ b/doc/source/api.rst @@ -0,0 +1,21 @@ +Library API +----------- + +libiio at its base is a C library. The API is designed to be as simple as possible, while still providing access to all the features of the IIO subsystem. Support for other languages is provided through bindings to the C API. + +This section provides a detailed description of the C API. The API is divided into several categories, each of which is described in a separate page. + +.. toctree:: + :maxdepth: 2 + + api/c/top + api/c/scan + api/c/attributes + api/c/context + api/c/device + api/c/channel + api/c/buffer + api/c/event + api/c/hwmon + api/c/debug + diff --git a/doc/source/api/c/attributes.rst b/doc/source/api/c/attributes.rst new file mode 100644 index 000000000..26d64f5b7 --- /dev/null +++ b/doc/source/api/c/attributes.rst @@ -0,0 +1,6 @@ +Attribute Functions +=================== + +.. doxygengroup:: Attributes + :content-only: + :members: diff --git a/doc/source/api/c/buffer.rst b/doc/source/api/c/buffer.rst new file mode 100644 index 000000000..d9df08ccd --- /dev/null +++ b/doc/source/api/c/buffer.rst @@ -0,0 +1,14 @@ +Buffer Functions +================== + +.. doxygengroup:: Buffer + :content-only: + :members: + +.. doxygengroup:: Block + :content-only: + :members: + +.. doxygengroup:: Stream + :content-only: + :members: diff --git a/doc/source/api/c/channel.rst b/doc/source/api/c/channel.rst new file mode 100644 index 000000000..11f1f1b20 --- /dev/null +++ b/doc/source/api/c/channel.rst @@ -0,0 +1,6 @@ +Channel Functions +================== + +.. doxygengroup:: Channel + :content-only: + :members: \ No newline at end of file diff --git a/doc/source/api/c/context.rst b/doc/source/api/c/context.rst new file mode 100644 index 000000000..7a96938f8 --- /dev/null +++ b/doc/source/api/c/context.rst @@ -0,0 +1,6 @@ +Context Functions +================== + +.. doxygengroup:: Context + :content-only: + :members: \ No newline at end of file diff --git a/doc/source/api/c/debug.rst b/doc/source/api/c/debug.rst new file mode 100644 index 000000000..d81f3faed --- /dev/null +++ b/doc/source/api/c/debug.rst @@ -0,0 +1,6 @@ +Debug and Low-Level Functions +============================= + +.. doxygengroup:: Debug + :content-only: + :members: \ No newline at end of file diff --git a/doc/source/api/c/device.rst b/doc/source/api/c/device.rst new file mode 100644 index 000000000..c7a5bcb66 --- /dev/null +++ b/doc/source/api/c/device.rst @@ -0,0 +1,6 @@ +Device Functions +================== + +.. doxygengroup:: Device + :content-only: + :members: \ No newline at end of file diff --git a/doc/source/api/c/event.rst b/doc/source/api/c/event.rst new file mode 100644 index 000000000..9bb6db5c6 --- /dev/null +++ b/doc/source/api/c/event.rst @@ -0,0 +1,6 @@ +Event Handling Functions +======================== + +.. doxygengroup:: Events + :content-only: + :members: \ No newline at end of file diff --git a/doc/source/api/c/hwmon.rst b/doc/source/api/c/hwmon.rst new file mode 100644 index 000000000..558756542 --- /dev/null +++ b/doc/source/api/c/hwmon.rst @@ -0,0 +1,6 @@ +Hardware Monitoring Devices +======================================================= + +.. doxygengroup:: Hwmon + :content-only: + :members: diff --git a/doc/source/api/c/scan.rst b/doc/source/api/c/scan.rst new file mode 100644 index 000000000..2642c5a1d --- /dev/null +++ b/doc/source/api/c/scan.rst @@ -0,0 +1,20 @@ +Scanning Functions +================== + +.. doxygenfunction:: iio_scan + + +.. doxygenfunction:: iio_scan_destroy + + +.. doxygenfunction:: iio_scan_get_results_count + + +.. doxygenfunction:: iio_scan_get_description + + +.. doxygenfunction:: iio_scan_get_uri + + +.. .. doxygenstruct:: iio_scan + diff --git a/doc/source/api/c/top.rst b/doc/source/api/c/top.rst new file mode 100644 index 000000000..ac4a6dfd9 --- /dev/null +++ b/doc/source/api/c/top.rst @@ -0,0 +1,6 @@ +Top-Level Functions +=================== + +.. doxygengroup:: TopLevel + :content-only: + :members: \ No newline at end of file diff --git a/doc/source/api/python/buffer.rst b/doc/source/api/python/buffer.rst new file mode 100644 index 000000000..6c9d1bb2a --- /dev/null +++ b/doc/source/api/python/buffer.rst @@ -0,0 +1,7 @@ +Buffer +================== + +Members +-------------- +.. autoclass:: iio.Buffer + :members: diff --git a/doc/source/api/python/channel.rst b/doc/source/api/python/channel.rst new file mode 100644 index 000000000..62a62fc64 --- /dev/null +++ b/doc/source/api/python/channel.rst @@ -0,0 +1,18 @@ +Channels +================== + +Members +-------------- +.. autoclass:: iio.Channel + :members: + +-------------------- + +Channel attributes +-------------------- +.. autoclass:: iio.DataFormat + :members: +.. autoclass:: iio.ChannelModifier + :members: +.. autoclass:: iio.ChannelType + :members: diff --git a/doc/source/api/python/context.rst b/doc/source/api/python/context.rst new file mode 100644 index 000000000..f1916a374 --- /dev/null +++ b/doc/source/api/python/context.rst @@ -0,0 +1,20 @@ +Contexts +================== + +Members +-------------- +.. autoclass:: iio.Context + :members: + +.. autoclass:: iio.LocalContext + :members: + :inherited-members: + +.. autoclass:: iio.XMLContext + :members: + :inherited-members: + +.. autoclass:: iio.NetworkContext + :members: + :inherited-members: + diff --git a/doc/source/api/python/device.rst b/doc/source/api/python/device.rst new file mode 100644 index 000000000..ae1410d3f --- /dev/null +++ b/doc/source/api/python/device.rst @@ -0,0 +1,20 @@ +Device +================== + +Members +-------------- +.. autoclass:: iio.Device + :members: + :inherited-members: + +------------------ + +.. Device attributes +.. ------------------ +.. .. autoclass:: iio.DeviceDebugAttr +.. :members: +.. :inherited-members: +.. .. autoclass:: iio.DeviceBufferAttr +.. :members: +.. :inherited-members: + diff --git a/doc/source/api/python/examples.rst b/doc/source/api/python/examples.rst new file mode 100644 index 000000000..280787aee --- /dev/null +++ b/doc/source/api/python/examples.rst @@ -0,0 +1,31 @@ +Examples +================== + + +Complete Application Examples +------------------------------ +.. toctree:: + iio_readdev + iio_writedev + iio_attr + iio_info + + +Code Snippets +----------------------------- + +Scan contexts and list channels of each device + +.. code-block:: python + + import iio + + for ctxname in iio.scan_contexts(): + ctx = iio.Context(ctxname) + for dev in ctx.devices: + if dev.channels: + for chan in dev.channels: + print("{} - {} - {}".format(ctxname, dev.name, chan._id)) + else: + print("{} - {}".format(ctxname, dev.name)) + diff --git a/doc/source/api/python/iio_attr.rst b/doc/source/api/python/iio_attr.rst new file mode 100644 index 000000000..ad722b302 --- /dev/null +++ b/doc/source/api/python/iio_attr.rst @@ -0,0 +1,9 @@ +iio_attr +====================== +| iio_attr is part of the libiio package, a library that has been developed to ease the development of software interfacing Linux Industrial I/O (IIO) devices. +| This tool is written using the libiio Python bindings. It works in a very similar way of how the base iio_attr works. You can find more information about it on this `page `_. + + +.. literalinclude:: ../../../../bindings/python/examples/iio_attr.py + :language: python + diff --git a/doc/source/api/python/iio_info.rst b/doc/source/api/python/iio_info.rst new file mode 100644 index 000000000..0f8256180 --- /dev/null +++ b/doc/source/api/python/iio_info.rst @@ -0,0 +1,10 @@ +iio_info +===================== +| iio_info is part of the libiio package, a library that has been developed to ease the development of software interfacing Linux Industrial I/O (IIO) devices. +| This tool is written using the libiio Python bindings. It works in the same way as the base iio_info works. You can find more information about it on this `page `_. + + + +.. literalinclude:: ../../../../bindings/python/examples/iio_info.py + :language: python + diff --git a/doc/source/api/python/iio_readdev.rst b/doc/source/api/python/iio_readdev.rst new file mode 100644 index 000000000..e23e01cef --- /dev/null +++ b/doc/source/api/python/iio_readdev.rst @@ -0,0 +1,9 @@ +iio_readdev +===================== +| iio_readdev is part of the libiio package, a library that has been developed to ease the development of software interfacing Linux Industrial I/O (IIO) devices. +| This tool is written using the libiio Python bindings. It works in the same way as the base iio_readdev works. You can find more information about it on this `page `_. + + +.. literalinclude:: ../../../../bindings/python/examples/iio_readdev.py + :language: python + diff --git a/doc/source/api/python/iio_writedev.rst b/doc/source/api/python/iio_writedev.rst new file mode 100644 index 000000000..5428f40f2 --- /dev/null +++ b/doc/source/api/python/iio_writedev.rst @@ -0,0 +1,9 @@ +iio_writedev +===================== +| iio_writedev is part of the libiio package, a library that has been developed to ease the development of software interfacing Linux Industrial I/O (IIO) devices. +| This tool is written using the libiio Python bindings. It works in the same way as the base iio_writedev works. You can find more information about it on this `page `_. + + +.. literalinclude:: ../../../../bindings/python/examples/iio_writedev.py + :language: python + diff --git a/doc/source/api/python/index.rst b/doc/source/api/python/index.rst new file mode 100644 index 000000000..b21722df6 --- /dev/null +++ b/doc/source/api/python/index.rst @@ -0,0 +1,47 @@ +Python Bindings +=============== + +Python bindings for the Industrial I/O interface library. + +Installation +############ + +The libiio python bindings can be installed from pip + +.. code-block:: bash + + (sudo) pip install pylibiio + +or by grabbing the source directly + +.. code-block:: bash + + git clone https://github.com/analogdevicesinc/libiio.git + cd bindings/python + (sudo) python3 setup.py install + +.. note:: + + On Linux the libiio python bindings are sometimes installed in locations not on path. On Ubuntu this is a common fix + + .. code-block:: bash + + export PYTHONPATH=$PYTHONPATH:/usr/lib/python{python-version}/site-packages + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + +Components +########## + +.. toctree:: + :maxdepth: 1 + + context + buffer + device + channel + trigger + examples + diff --git a/doc/source/api/python/trigger.rst b/doc/source/api/python/trigger.rst new file mode 100644 index 000000000..6ddc1453d --- /dev/null +++ b/doc/source/api/python/trigger.rst @@ -0,0 +1,8 @@ +Trigger +================== + +Members +-------------- +.. autoclass:: iio.Trigger + :members: + :inherited-members: diff --git a/doc/source/bindings.rst b/doc/source/bindings.rst new file mode 100644 index 000000000..b382778a6 --- /dev/null +++ b/doc/source/bindings.rst @@ -0,0 +1,8 @@ +Bindings +======== + + +.. toctree:: + :maxdepth: 2 + + api/python/index \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 000000000..df4c22c6d --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,80 @@ +import datetime +import os + +# -- Pre-build tasks ---------------------------------------------------------- +# Build doxygen XML documentation + +doxyfolder = os.path.join(os.path.dirname(__file__), "..", "doxygen") +cwd = os.getcwd() +os.chdir(doxyfolder) +os.system("doxygen doxyfile.in") +os.chdir(cwd) + +# Add bindings and examples to path +import sys +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "bindings", "python"))) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "bindings", "examples"))) + +import iio + +# -- Project information ----------------------------------------------------- +project = 'libiio' +year = datetime.datetime.now().year + +copyright = f'2015-{year}, Analog Devices, Inc.' +author = 'Analog Devices, Inc.' +release = 'v1.0' + +# -- General configuration --------------------------------------------------- + +extensions = [ + "sphinx.ext.todo", + "adi_doctools", + "myst_parser", + "breathe", + 'sphinxcontrib.matlab', 'sphinx.ext.autodoc', + "sphinx_inline_tabs", +] + +needs_extensions = { + 'adi_doctools': '0.3.6' +} + +myst_enable_extensions = ["colon_fence", "attrs_inline"] + +breathe_default_project = "libiio" +breathe_projects = {"libiio": os.path.join(doxyfolder, "generated", "xml")} + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +source_suffix = ['.rst', '.md'] + +matlab_src_dir = os.path.join(os.path.dirname(__file__), "..", '..') +# matlab_keep_package_prefix = False +matlab_short_links = True + +# -- External docs configuration ---------------------------------------------- + +interref_repos = ['doctools'] + +# -- Custom extensions configuration ------------------------------------------ + +hide_collapsible_content = True +validate_links = False + +# -- todo configuration ------------------------------------------------------- + +todo_include_todos = True +todo_emit_warnings = True + +# -- Options for HTML output -------------------------------------------------- + +html_theme = 'cosmic' +html_static_path = ['_static'] +html_css_files = ["custom.css"] +# html_favicon = path.join("sources", "icon.svg") + +# html_theme_options = { +# "light_logo": "HDL_logo_cropped.svg", +# "dark_logo": "HDL_logo_w_cropped.svg", +# } \ No newline at end of file diff --git a/doc/source/examples.md b/doc/source/examples.md new file mode 100644 index 000000000..9ad8cd68b --- /dev/null +++ b/doc/source/examples.md @@ -0,0 +1,71 @@ +# Examples + +This page provides a few examples of how to use the libiio library. The examples are written in C and are intended to be used as a starting point for your own application. For other languages, please refer to the [Bindings](bindings.rst) page. + +:::{note} +To reduce verbosity, the error handling code has been omitted from the examples. In a real application, you should always check the return value of each function call. +::: + +## Connect to Ethernet Context and List Devices + +```c +#include +#include + +int main() { + + struct iio_context *ctx; + struct iio_device *dev; + struct iio_device **devices; + int i, ndevices; + + ctx = iio_create_context(NULL, "ip:analog.local"); + ndevices = iio_context_get_devices_count(ctx); + for (i = 0; i < ndevices; i++) { + dev = iio_context_get_device(ctx, i); + printf("Device %d: %s\n", i, iio_device_get_name(dev)); + } + + iio_context_destroy(ctx); + + return 0; +} +``` + +## Write Device and Channel Attribute + +```c + +#include +#include + +int main() { + + struct iio_context *ctx; + struct iio_device *dev; + struct iio_channel *ch; + struct iio_attr *attr; + + ctx = iio_create_context(NULL, "ip:analog.local"); + dev = iio_context_find_device(ctx, "ad9361-phy"); + attr = iio_device_find_attr(dev, "ensm_mode"); + iio_attr_write(attr, "fdd"); + + ch = iio_device_find_channel(dev, "voltage0", false); + attr = iio_channel_find_attr(ch, "hardwaregain"); + iio_attr_write(attr, "0"); + + iio_context_destroy(ctx); + + return 0; +} +``` + +## Device Specific Examples + +The following examples are available in the libiio repository: + +- [ad9361-iiostream.c](https://github.com/analogdevicesinc/libiio/blob/main/examples/ad9361-iiostream.c) +- [ad9361-iio-stream.c](https://github.com/analogdevicesinc/libiio/blob/main/examples/ad9371-iiostream.c) +- [adrv9002-iiostream.c](https://github.com/analogdevicesinc/libiio/blob/main/examples/adrv9002-iiostream.c) +- [adrv9009-iiostream.c](https://github.com/analogdevicesinc/libiio/blob/main/examples/adrv9009-iiostream.c) diff --git a/doc/source/iiod.md b/doc/source/iiod.md new file mode 100644 index 000000000..78aeb2965 --- /dev/null +++ b/doc/source/iiod.md @@ -0,0 +1,4 @@ +# iiod: libiio Server Daemon + +The `iiod` is a server daemon that provides a remote interface to the libiio library. It is designed to run on a Linux host, and can communicate with libiio clients over a network, USB, or serial link. For deeply embedded, resource-constrained systems, a separate server called `tiny-iiod` is available. + diff --git a/doc/source/index.md b/doc/source/index.md new file mode 100644 index 000000000..a2db23cf4 --- /dev/null +++ b/doc/source/index.md @@ -0,0 +1,58 @@ +# libiio + +Thanks for your interest in the libiio, a C/C++ library that provides generic access to Industrial Input Output (IIO) +devices. IIO started as a [Linux kernel subsystem](https://www.kernel.org/doc/html/latest/driver-api/iio/index.html) to support for devices that included analog-to-digital converters (ADCs) and/or digital-to-analog converters (DACs). While the libiio continues to provide an easy interface to the Linux kernel IIO subsystem, it has also expanded beyond that, and is now just as common to see this used inside an embedded system or hypervisor as it is on a host PC. + +It is portable: Using a single cross-platform API, it provides access to IIO devices on Linux, macOS, Windows, etc across local and remote (USB, Network, Serial) devices. The library is composed by one high-level API, and several backends: +- the *local* backend, which interfaces the Linux kernel through the sysfs virtual filesystem, +- the *remote* backend, which communicates to an *iiod* server through a network, usb or serial ([wired](https://en.wikipedia.org/wiki/RS-232), [USB CDC](https://en.wikipedia.org/wiki/USB_communications_device_class), or [Bluetooth SPP](https://en.wikipedia.org/wiki/List_of_Bluetooth_profiles#Serial_Port_Profile_(SPP))) link. The *iiod* server can run on a: + - Linux host (an [IIO daemon](https://github.com/analogdevicesinc/libiio/tree/master/iiod) is included part of libiio), this would typically communicate to a libiio client over the network or USB; or + - a deeply embedded, resource constrained system (like Arduino) managed separately as [tiny-iiod](https://github.com/analogdevicesinc/libtinyiiod); this would typically communicate to a libiio client over the network, or serial. + +It is entirely user-mode: No special privilege or elevation is required for the application to communicate with a device. One of the most powerful things about libiio is its [Remote Procedure Call](https://en.wikipedia.org/wiki/Remote_procedure_call) style interface. Moving backends from USB to Networking to local embedded does not require any code changes. The users of the libiio do not need to code any differently for the remote interaction, making it easy to move from remote (debug on PC over Ethernet) to local (deployed on embedded Linux). + +## What platforms are supported? + +Any host running Linux, macOS, Windows, or OpenBSD/NetBSD, should be trivial to get libiio running on. If you are interested in porting to other hosts that support either networking (socket interface), [libusb](https://libusb.info/) or serial, it should be very straightforward. [Pull Requests](https://github.com/analogdevicesinc/libiio/pulls) are always reviewed, and well written ones are normally accepted. + +The local backend and Linux daemon can run on any embedded Linux based system, from purpose built systems like [PlutoSDR](https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/ADALM-PLUTO.html) or [ADALM2000](https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/ADALM2000.html) to [Raspberry Pi](https://www.raspberrypi.org/) or [BeagleBoard](https://beagleboard.org/) to [Jetson](https://www.nvidia.com/en-us/autonomous-machines/embedded-systems/). [tiny-iiod](https://github.com/analogdevicesinc/libtinyiiod) requires a modern C compiler and is known to work on a variety of non-Linux frameworks including [Mbed](https://www.mbed.com/) and [FreeRTOS](https://www.freertos.org/). + +## Sounds good! How do I get started? + +If you are using Linux, chances are your distribution already includes libiio, so you probably just need to reference the `iio.h` header in your source. + +For other platforms, you are encouraged to use one of our [release builds](https://github.com/analogdevicesinc/libiio/releases/latest). If you want to use the very latest, you have the option to use a nightly build or build from source. Please check the Downloads menu. + +If you prefer, you can also access the source directly from [github](https://github.com/analogdevicesinc/libiio). + +Once you have secured your access to the library and its header, please check the [libiio API](api.rst) or the [libiio examples](examples.md). + +## Where is (insert my favourite language) support? + +The mainline library is written in C, and has built in bindings for C++, Python and C# (C-Sharp). [Node.js](https://github.com/drom/node-iio) and [Rust](https://github.com/fpagliughi/rust-industrial-io) are maintained outside the main repo. If you are interested in creating more language bindings, please [reach out](https://github.com/analogdevicesinc/libiio/issues) to the developers by posting an issue on github. + +# Licensing + +libiio has been developed and is released under the terms of the GNU Lesser General Public License, version 2 or (at your option) any later version. This open-source license allows anyone to use the library for proprietary or open-source, commercial or non-commercial applications. + +Separately, the IIO Library also includes a set of test examples and utilities, (collectively known as iio-utils) which are developed and released under the terms of the GNU General Public License, version 2 or (at your option) any later version. + +The full terms of the library license can be found at: http://opensource.org/licenses/LGPL-2.1 and the iio-utils license can be found at: https://opensource.org/licenses/GPL-2.0 + +# Project Pages + +```{toctree} +:maxdepth: 1 + +install +theory +usage +examples +api +bindings +tools/index +iiod +training +related + +``` diff --git a/doc/source/install.md b/doc/source/install.md new file mode 100644 index 000000000..e74d5544d --- /dev/null +++ b/doc/source/install.md @@ -0,0 +1,45 @@ +# Installation + +It is recommended to use the latest releases through pre-built packages when available. If you need the latest features or are developing libiio itself, you can build from source following guides: + +```{toctree} +:maxdepth: 1 + +install/source +``` + +## Installation Packages + +Pre-built packages are available for the following for a number of different platforms from GitHub as well as from different package managers. + +### Windows + +Install the latest release from the [GitHub releases page](https://github.com/analogdevicesinc/libiio/releases). + +The EXE installer is recommend but zip packages include the same files, which can be useful for developers. + + +### Linux + +Most Linux users can install libiio from their distribution's package manager. For example, on Ubuntu, you can install libiio with the following command: + +```bash +sudo apt-get install libiio0 +``` + +It can be also useful to install the development package and tools: + +```bash +sudo apt-get install libiio-dev libiio-utils +``` + +Please reference your OS's package manager for the correct package names. Alternatively, you can download the latest release from the [GitHub releases page](https://github.com/analogdevicesinc/libiio/releases) or build from source. + +### macOS + +For macOS there are four options: + +- [Homebrew](#homebrew) +- [MacPorts](https://ports.macports.org/port/libiio/) +- DMG installer from the [GitHub releases page](https://github.com/analogdevicesinc/libiio/releases) +- [Building from source](install/source.md) diff --git a/doc/source/install/source.md b/doc/source/install/source.md new file mode 100644 index 000000000..c558434b5 --- /dev/null +++ b/doc/source/install/source.md @@ -0,0 +1,269 @@ +# Building from Source + +:::{warning} + +libiio v1, available in the `main` branch, is under heavy active development. If you are looking for a stable version, please use the latest release by checking out a tag starting with v0.. For example, to check out the v0.25 release, use the following command: + +```bash +git checkout v0.25 +``` + +::: + +This section describes how to build libiio from source. This is useful if you want to use the latest features or if you are developing libiio itself. + +## Install Prerequisites/Dependencies and Build + +````{tab} Linux (Debian/Ubuntu) + +Basic system setup +```shell +analog@precision:~$ sudo apt-get update +analog@precision:~$ sudo apt-get install build-essential +``` +Install Prerequisites +```shell +analog@precision:~$ sudo apt-get install libxml2-dev libzstd-dev bison flex libcdk5-dev cmake +``` +Install libraries for Backends +```shell +analog@precision:~$ sudo apt-get install libaio-dev libusb-1.0-0-dev +analog@precision:~$ sudo apt-get install libserialport-dev libavahi-client-dev +``` +Install to build doc +```shell +analog@precision:~$ sudo apt-get install doxygen graphviz +``` +Install to build python backends +```shell +analog@precision:~$ sudo apt-get install python3 python3-pip python3-setuptools +``` + + +### Clone +```shell +analog@precision:~$ git clone https://github.com/analogdevicesinc/libiio.git +analog@precision:~$ cd libiio +``` + +### Build +```shell +analog@precision:~/libiio$ mkdir build +analog@precision:~/libiio$ cd build +analog@precision:~/libiio/build$ cmake ../ +analog@precision:~/libiio/build$ make +``` + +For detailed information about the different build options, see the [Configuration Options](#configuration-options) section. + +### Install +```shell +analog@precision:~/libiio/build$ sudo make install +``` + +:::{note} +Some things (specifically building doc) need to find libiio or the bindings on path. +That means that you configure (with -DWITH_DOC=OFF), build, install, configure +(with -DWITH_DOC=ON), build again to get the doc. If you have issues, please ask. +::: + + +```` + + +````{tab} Windows + +On Windows you will need to install a supported compiler such as Mingw-w64 or Visual Studio. Typically, Visual Studio will include CMake, but if you are using Mingw-w64, you will need to install it separately within the Mingw-w64 environment. + +You will also need to install the following dependencies: +- libxml2 +- libzstd +- (Optional) libusb +- (Optional) libserialport + +These are available in the [following zip for convenience](http://swdownloads.analog.com/cse/build/libiio-deps-20220517.zip). + +```powershell +mkdir C:\libiio-deps +cd C:\libiio-deps +Invoke-WebRequest -Uri http://swdownloads.analog.com/cse/build/libiio-deps-20220517.zip -OutFile libiio-deps-20220517.zip +Expand-Archive -Path libiio-deps-20220517.zip -DestinationPath . +``` + +### Clone +```powershell +C:\> mkdir dev +C:\> cd dev +C:\dev> git clone https://github.com/analogdevicesinc/libiio.git +C:\dev> cd libiio +``` + +:::{note} +For Visual Studio, you will need to run the following command to set up the environment: +```powershell +C:\> "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" +``` +This may be different depending on your version of Visual Studio. You can also use the Developer Command Prompt for Visual Studio. +::: + + +### Build +```powershell +C:\dev\libiio> mkdir build +C:\dev\libiio> cd build +C:\dev\libiio\build> cmake -DWITH_USB_BACKEND=ON -DLIBXML2_LIBRARIES="C:\\libiio-deps\\libs\\64\\libxml2.lib" -DLIBUSB_LIBRARIES="C:\\libiio-deps\\libs\\64\\libusb-1.0.lib" -DLIBSERIALPORT_LIBRARIES="C:\\libiio-deps\\libs\\64\\libserialport.dll.a" -DLIBZSTD_LIBRARIES="C:\\libiio-deps\\libs\\64\\libzstd.dll.a" -DLIBUSB_INCLUDE_DIR="C:\\libiio-deps\\include\\libusb-1.0" -DLIBXML2_INCLUDE_DIR="C:\\libiio-deps\\include\\libxml2" -DLIBZSTD_INCLUDE_DIR="C:\\libiio-deps\\include" .. + +C:\dev\libiio\build> cmake --build . --config Release + +```` + +````{tab} macOS + +This process relies on [Homebrew](https://brew.sh/), a package manager for macOS. If you don't have Homebrew installed, you can install it by running the following command in your terminal: + +```shell +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +``` + +(homebrew)= +### Easy: Using a homebrew formula + +```shell +brew install tfcollins/libiio +``` + +### Manual: Install Prerequisites +```shell +brew install cmake libxml2 libzstd libusb libserialport zstd +``` + +Optional dependencies for building documentation and python bindings: +```shell +brew install doxygen ncurses cdk sphinx-doc pkg-config +pip3 install sphinx setuptools +``` + +### Clone +```shell +mkdir /tmp/dev +cd /tmp/dev +git clone https://github.com/analogdevicesinc/libiio.git +``` + +### Build +```shell +cd libiio +mkdir build +cd build +cmake .. +make +``` + +### Install +```shell +sudo make install +``` + +--- + +(configuration-options)= +## Configuration Options + +when configuring libiio with cmake, there are a few optional settings that you can use to control the build. +The recommendation is to leave things to the default. + +Cmake Options | Default | Target | Description | +---------------------- | ------- | -------| ---------------------------------------------- | +`BUILD_SHARED_LIBS` | ON | All | Build shared libraries | +`libiio_COMPAT` | ON | All | Build libiio v0.x compatibility layer | +`WITH_MODULES` | OFF | All | Build modular backends | +'COMPILE_WARNING_AS_ERROR' | OFF | All | Make all C warnings into errors | +`CPP_BINDINGS` | OFF | All | Install C++ bindings (C++17 required for examples) | +`PYTHON_BINDINGS` | OFF | All | Install PYTHON bindings | +`WITH_UTILS` | ON | All | Build the utility programs (iio-utils) | +`WITH_EXAMPLES` | OFF | All | Build the example programs | +`NO_THREADS` | OFF | All | Disable multi-threading support | +`CSHARP_BINDINGS` | OFF | Windows | Install C## bindings | +`CMAKE_INSTALL_PREFIX` | `/usr` | Linux | default install path | +`ENABLE_PACKAGING` | OFF | Linux, MaC | Create .deb/.rpm or .tar.gz packages via 'make package' | +`WITH_DOC` | OFF | Linux | Generate documentation with Doxygen and Sphinx | +`WITH_MAN` | OFF | Linux | Generate and install man pages | +`INSTALL_UDEV_RULE` | ON | Linux | Install a Linux udev rule for detection of USB devices | +`UDEV_RULES_INSTALL_DIR` | /lib/udev/rules.d | Linux | default install path for udev rules | +`WITH_LOCAL_CONFIG` | ON | Linux | Read local context attributes from /etc/libiio.ini | +`WITH_HWMON` | ON | Linux | Add compatibility with the hwmon subsystem | +`WITH_GCOV` | OFF | Linux | Build with gcov profiling flags | +`OSX_FRAMEWORK` | ON | Mac | OS X frameworks provide the interfaces you need to write software for Mac. | +`OSX_PACKAGE` | ON | Mac | Create a OSX package for installation on local and other machines | + +Which backends the library supports is dependent on the build system, but can be overridden. +(If cmake finds libusb, it will use it, unless turned off manually) + +Cmake Options | Default | Depends on | Description | +---------------------- | ------- | ------------- | ------------------------------- | +`WITH_XML_BACKEND` | ON | libxml2 | Enable the XML backend, required when using network, serial, or USB backend | +`WITH_USB_BACKEND` | ON | libusb | Enable the libusb backend | +`WITH_USB_BACKEND_DYNAMIC` | ON | Modules + USB backend | Compile the USB backend as a module | +`WITH_SERIAL_BACKEND` | OFF | libserialport | Enable the Serial backend | +`WITH_SERIAL_BACKEND_DYNAMIC` | ON | Modules + serial backend | Compile the serial backend as a module | +`WITH_NETWORK_BACKEND` | ON | | Supports TCP/IP | +`WITH_NETWORK_BACKEND_DYNAMIC` | ON | Modules + network backend | Compile the network backend as a module | +`WITH_EXTERNAL_BACKEND` | OFF | | Support external backend provided by the application | +`HAVE_DNS_SD` | ON | Networking | Enable DNS-SD (ZeroConf) support | +`ENABLE_IPV6` | ON | Networking | Define if you want to enable IPv6 support | +`WITH_LOCAL_BACKEND` | ON | Linux | Enables local support with iiod | +`WITH_LOCAL_CONFIG` | ON | Local backend | Read local context attributes from /etc/libiio.ini | + + +There are a few options, which are experimental, which should be left to their default settings: + +Cmake Options | Default | Description | +------------------- | ------- | ---------------------------------------------- | +`WITH_LOCAL_MMAP_API` | ON | Use the mmap API provided in Analog Devices' kernel (not upstream) | +`WITH_LOCAL_DMABUF_API` | ON | Use the experimental DMABUF interface (not upstream) | +`WITH_ZSTD` | ON | Support for ZSTD compressed metadata | + +Developer options, which either increases verbosity, or decreases size. It can +be useful to keep track of things when you are developing with libiio to print +out warnings, to better understand what is going on. Most users should leave it +at 'Error' and Embedded Developers are free to set it to 'NoLog' to save space. +this is invoked as "-DLOG_LEVEL=Debug". + +Cmake Options | Default | Description | +----------------- | ------- | ---------------------------------------------- | +| | | NoLog : Remove all warning/error messages | +|`LOG_LEVEL` | | Error : Print errors only | +| | | Warning : Print warnings and errors | +| | Info | Info : Print info, warnings and errors | +| | | Debug : Print debug/info/warnings/errors (very verbose) | + +Options which effect iiod only. These are only available on Linux. + +Cmake Options | Default | Description | +------------------- | ------- | ---------------------------------------------- | +`WITH_IIOD` | ON | Build the IIO Daemon | +`WITH_IIOD_NETWORK` | ON | Add network (TCP/IP) support | +`WITH_IIOD_SERIAL` | ON | Add serial (UART) support | +`WITH_IIOD_USBD` | ON | Add support for USB through FunctionFS within IIOD | +`WITH_IIOD_V0_COMPAT` | ON | Add support for libiio v0.x protocol and clients | +`WITH_LIBTINYIIOD` | OFF | Build libtinyiiod | +`WITH_AIO` | ON | Build IIOD with async. I/O support | +`WITH_SYSTEMD` | OFF | Enable installation of systemd service file for iiod | +`SYSTEMD_UNIT_INSTALL_DIR` | /lib/systemd/system | default install path for systemd unit files | +`WITH_SYSVINIT` | OFF | Enable installation of SysVinit script for iiod | +`SYSVINIT_INSTALL_DIR` | /etc/init.d | default install path for SysVinit scripts | +`WITH_UPSTART` | OFF | Enable installation of upstart config file for iiod | +`UPSTART_CONF_INSTALL_DIR`: | /etc/init | default install path for upstart conf files | + +## Notes + +Special Note on Microsoft Visual Compiler (MSVC): + +MSVC in debug mode is just very stupid. If it sees this code: +```c +if (0) + call_function(); +``` +It will try to link against the `call_function` symbol even though it's clearly dead code. + +For this reason, when building with MSVC, please build in `RelWithDebInfo` mode. If you try to build in `Debug` mode, it will error. diff --git a/doc/source/related.md b/doc/source/related.md new file mode 100644 index 000000000..5455cab8c --- /dev/null +++ b/doc/source/related.md @@ -0,0 +1,12 @@ +# Related Projects + +- [IIO-Oscilloscope](https://wiki.analog.com/resources/tools-software/linux-software/iio_oscilloscope) +- [pyadi-iio](https://analogdevicesinc.github.io/pyadi-iio/) +- MATLAB Toolboxes + - [Analog Devices Transceiver Toolbox](https://analogdevicesinc.github.io/TransceiverToolbox/master/) + - [Analog Devices High Speed Converter Toolbox](https://analogdevicesinc.github.io/HighSpeedConverterToolbox/master/) + - [Analog Devices RF and Microwave Toolbox](https://github.com/analogdevicesinc/RFMicrowaveToolbox) + - [Analog Devices Precision Toolbox](https://analogdevicesinc.github.io/PrecisionToolbox/) +- [Scopy](https://wiki.analog.com/university/tools/m2k/scopy) +- [libad9361-iio](https://analogdevicesinc.github.io/libad9361-iio) +- [libad9166-iio](https://github.com/analogdevicesinc/libad9166-iio) diff --git a/doc/source/theory.md b/doc/source/theory.md new file mode 100644 index 000000000..9f09ff0e9 --- /dev/null +++ b/doc/source/theory.md @@ -0,0 +1,804 @@ +# Theory of Operation + +## Objectives + +Why did Analog Devices develop libiio? +To answer that question, it is needed to understand the state of things before libiio was introduced. + +### Simplification and standardization + +The Linux kernel features a IIO subsystem, which provides a standardized interface with the +user-space for client applications. The various drivers designed to interface different IIO devices +register with the IIO subsystem. As a result, all supported devices can be used by user-space +applications with the same interface. + +At least, that\'s the theory. The reality is more complex; the interface provides different ways to +read or write a device, each driver generally implementing only the oldest and slowest method. The +various drivers will also create slightly different files in the sysfs filesystem of Linux, where all the +virtual files used to configure the interface are present. + +As a result, before libiio was born the applications using IIO devices would generally be +designed to support only one particular device, because it was too much work to support several at a +time. Because of this, a lot of applications had their own code to interface with the kernel\'s IIO subsystem leading to maintenance issues. Furthermore, it was very difficult for customers to +create applications to use their hardware since they had to continuously rewrite the interface code or adapt it +from a pre-existing application. + +The objective behind libiio is to ease the development process of applications using IIO devices, +by letting the new library be the intermediate between the program and the kernel. + +By cleverly identifying devices, available input or output channels, libiio allows one application +to support a wide range of devices. For instance, if the application requests one device that features +a capture channel without specifying its name, then it will be compatible with all IIO devices with +at least one capture channel that exist to date, as well as future hardware that has yet to be invented. + +### Advanced features + +Beyond resolving issues, libiio was also announcing new features. The major planned +improvement being a network backend, which would broaden the set of possibilities: running the +applications as a single user, reading one IIO device from different applications, and of course use a +device from anywhere in the network in an application. + +That network backend was also opening questions about other possible improvements: for +instance, using IIO devices from applications running on different operating systems, like Windows, +using those devices in environments like [GNU Radio](https://wiki.analog.com/resources/tools-software/linux-software/gnuradio), [MATLAB](https://wiki.analog.com/resources/tools-software/mathworks) or [Simulink](https://wiki.analog.com/resources/tools-software/mathworks), etc. + + + +## License and code management + +libiio has been developed and is released under the terms of the GNU Lesser General Public +License, version 2. This open-source license allows anyone to use the library for proprietary or +open-source, commercial or non-commercial applications. This choice was motivated by the fact +that Analog Devices is a company that principally sells hardware, and this library provides the +clients with a better and easier way of using this hardware. + +The full terms of the license can be found here: + +## Code conformance + +A good part of libiio fully complies with the C99 and POSIX standards, and should be +compilable on any POSIX-compliant operating system supported by a C99 compiler. The exception +is the local backend, which is Linux-specific and thus can only be compiled for Linux systems. +The library can also be compiled under Visual Studio, which does not fully support C99 (in +2014\...), by preferring ANSI C89 when it makes sense. As the conformance to C99 and POSIX was +a design decision since the beginning of the project, porting the library to Windows has been +exceptionally easy and required very little change, thanks to the recent POSIX sockets compatibility +layer provided in Windows. + +## Code visibility + +While the public API declares and references iio_context, iio_device, iio_channel and iio_buffer +objects, their content is never known to the client application. All the public functions use pointers +to opaque structures: + +``` c +struct iio_context; +__api void iio_context_destroy(struct iio_context *context); +``` + +In this example, the content of the iio_context structure is never listed, but the +iio_context_destroy function accepts a pointer to an instance of that structure as a parameter. This +design choice has several benefits: + +- The client applications are not able to modify directly the content of the objects; they must use the public functions of the API instead. This prevents the applications to mess with the internals of the library. +- As the client applications manipulate only pointers, the structure of the object can change from one version of the library to another without breaking the ABI (binary interface). As a result, an old program compiled with an old version of the library will run just fine with a new version of the library, even if the structure of all the objects changed in the new version. This brings a lot of flexibility, as a client software isn\'t bound to one specific version of the API. + +On top of that, the functions that compose the API of libiio are all prefixed with "\_\_api". This +token marks the corresponding functions as visible in the library. The functions that are not marked +are considered hidden and will not be callable from outside the library. This ensures that client +applications cannot call internal functions, and use the API functions instead. + +## Backends + +The libiio library has been designed from the start to support multiple backends. The current 0.1 +version features three different backends: a XML backend, a local backend, a network backend. +Concretely, a backend is assimilated with a iio_context object. For each backend, one function in +the public API allows the creation of the corresponding iio_context object: + +``` c +__api struct iio_context * iio_create_local_context(void); +__api struct iio_context * iio_create_xml_context(const char *xml_file); +__api struct iio_context * iio_create_xml_context_mem(const char *xml, size_t len); +__api struct iio_context * iio_create_network_context(const char *host); +``` + +The iio_context object contains a pointer to a iio_backend_ops structure: + +``` c +struct iio_backend_ops { + ssize_t (*read)(const struct iio_device *dev, void *dst, size_t len, uint32_t *mask, size_t words); + ssize_t (*write)(const struct iio_device *dev, const void *src, size_t len); + ... +}; +``` + +This structure contains a set of function pointers that correspond to low-level functionalities: +read an attribute, open a device, stream data\... Those functions are specific to the backend used. For +instance, the "read" function pointer will be empty with the XML backend (as it does not support +streaming data), point to a function to retrieve a buffer full of freshly captured samples from the +Linux kernel with the local backend, or point to a function to send a read request via the network +with the network backend. + +One thing to consider is that it is not a requirement to have all the backends enabled for the +library to work. For instance, the Windows versions are built without the local backend, as this one +is Linux-specific; and a build of the library meant to run on a development board with IIO devices +attached could be compiled without the network and XML backends, as those would be unused. + +## Layering + +The libiio library is built with two distincts layers. The top layer contains the implementations of +all the public functions. It is high-level, in the sense that those functions can be used independently +of the backend that was used to create the iio_context object. Those functions only use the content +of the various objects of the context, and rely on the functions provided by the backend in its +"iio_backend_ops" structure to perform any low-level operation. Those backend-specific functions +form the bottom layer of the library. + +Here is a short example, extracted from the source code of libiio, that shows how the functions +that compose the public API of the library can call the backend-provided functions to perform +operations: + +``` c +int iio_device_close(const struct iio_device *dev) +{ + if (dev->ctx->ops->close) + return dev->ctx->ops->close(dev); + else + return -ENOSYS; +} +``` + +The iio_device_close function is part of the public API. What it does, is just call the "close" +function provided by the backend if available. Of course, this function\'s implementation is very +simple, but some other high-level functions perform much more than just a call to a backend +function. + +The direct consequence of having proper code visibility and layering, is that one application that +use the libiio library and that was designed with the local backend, will work remotely through the +network just by changing one line of code: create the iio_context object with +iio_create_network_backend instead of iio_create_local_backend. All the functions that the +application will call will have the exact same effect, but with a different behaviour. + +## The XML and local backends + +### The XML backend + +One of the first things implemented in the library has been the XML backend. Using this +backend, it is possible to generate a libiio context from a pre-existing XML file with a specific +structure. This backend has been very handy in the beginning of the development process, for the +simple reason that it simplifies the task of validating the code model: a XML file generated from the +code model with the iio_context_get_xml public function must be parsable, usable by the XML +backend, and result in the exact same objects being re-created. + +The XML backend is the simplest backend. For instance, it does not provide any low-level +function to read or write attributes, or stream data. The full C code of this backend fits in around +360 lines, so it is extremely small. It uses the libxml2 library, available under Unix operating +systems as well as Windows and with a compatible license (LGPL) to validate then parse the XML +file. + +#### Document Type Definition + +```xml + + + + + + + + + + + + + + ]> +``` + +This DTD corresponds to the format expected by the XML backend of the latest libiio. It is +always embedded at the top of the XML generated with iio_context_get_xml, and the XML backend +will verify that the given XML file validates with the embedded format and issue an error if it\'s not +the case. + +The format of the XML evolved quite a lot during the development phase of the library, to reflect +the new features implemented in the process. The "scan-element" and "debug-attribute" elements, +for instance, were added very late in the development phase. + +### The local backend + +The central and most complex piece of the libiio library is the local backend. This may be the +single most important part of the library, as it is the only part that will actually interact with the +hardware through the sysfs interface of the Linux kernel. + +#### Creation of the context from sysfs + +The first task, when creating the local backend, has been to implement the function +iio_create_local_context, the one responsible for the creation of the top-level iio_context object. +The implementation of this functionality represents maybe half of the complexity of the local +backend. To understand why, let\'s have a look at how the generation of the objects from sysfs +works. + +Here is a list of files that can be found inside /sys/bus/iio/devices 1 : + + 1: /sys/bus/iio/devices/iio:device0/name + 2: /sys/bus/iio/devices/iio:device0/out_voltage0_V1_raw + 3: /sys/bus/iio/devices/iio:device0/out_voltage0_V1_scale + 4: /sys/bus/iio/devices/iio:device0/out_voltage0_V1_powerdown + 5: /sys/bus/iio/devices/iio:device0/out_voltage0_V1_powerdown_mode + 6: /sys/bus/iio/devices/iio:device0/out_voltage1_V2_raw + 7: /sys/bus/iio/devices/iio:device0/out_voltage1_V2_scale + 8: /sys/bus/iio/devices/iio:device0/out_voltage1_V2_powerdown + 9: /sys/bus/iio/devices/iio:device0/out_voltage1_V2_powerdown_mode + 10: /sys/bus/iio/devices/iio:device0/out_voltage_powerdown_mode_available + 11: /sys/bus/iio/devices/iio:device0/sampling_rate + 12: /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_en + 13: /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_index + 14: /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_type + 15: /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage1_en + 16: /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage1_index + 17: /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage1_type + +The local backend will perform the following steps: + +- **Identify the IIO devices that can be used**. In this case, only one device is present, so one single iio_device object will be created, with the ID "iio:device0" and the name corresponding to the text contained in the "name" attribute (line 1). +- **Identify the various channels for each devices**. In this example, the device features four different channels: two output channels with the IDs "voltage0" and "voltage1", and two input channels with the IDs "voltage0" and "voltage1". Note that the IDs can be the same as long as their direction is opposed, which is the case here. The correct way to identify a channel is by looking at their ID and their direction.\ + The two output channels also have a name: "V1" and "V2". Having a name is optional, that\'s why the two input channels don\'t have any.\ + Note that the two input channels are located in a sub-directory called "scan_elements". All the files present in that directory correspond to channels which support streaming (either capturing samples from the device for input channels, or submitting samples to the device for output channels). +- **Identify the channel-specific attributes**. For instance, each one of the two output channels will have the "raw", "scale", "powerdown" and "powerdown_mode" attributes, most likely containing distinct values. +- **Identify the attributes shared by all channels**. In this example, the two output channels will share one "powerdown_mode_available" attribute; if one channel modifies the content of the attribute, it is modified for all the channels of the device that have this attribute. +- **Identify the device-specific attributes**, so the ones that don\'t apply to channels at all. In this case, this device has one attribute named "sample_rate". In theory, it also has the "name" attribute, but this one is not registered as an attribute in the iio_device object. + +Additionally, the local backend will also add "debug attributes" to any device whose ID is found in the /sys/kernel/debug/iio directory. Those attributes can be assimilated to device-specific attributes, their only specificity is that they may not be available all the time. This directory being only browsable by the "root" super-user, they may be found only if the application is started with super-user rights. + +#### Parsing issues + +Although the kernel interface has been designed to be simple, it has not been designed to be +parsable. Let\'s take an example: + + 1: /sys/bus/iio/devices/iio:device0/out_voltage0_V1_raw + 2: /sys/bus/iio/devices/iio:device0/out_voltage0_V1_scale + 3: /sys/bus/iio/devices/iio:device0/out_voltage1_V2_raw + +The name of the "voltage0" channel is correctly detected as being "V1", and the two "raw" and +"scale" attributes are detected properly. However, for the channel "voltage1", it is not possible to +differenciate the channel name with the attribute name: maybe the name is "V2" and the attribute +name is "raw", but maybe the channel has no name and the attribute is named "V2_raw"\... + +The sad fact is that there is no easy way to address this issue; it has been avoided so far, as +channels often have more than one file in sysfs and rarely have names. The issue is still present in +the latest version of libiio, and will probably be worked around by using a database to map the +filenames in sysfs to the corresponding device, channel and attribute. + +The issue becomes even more complex if you consider that the filenames can include "modifiers". The modifiers are known tokens in filenames, the local backend of the library uses a built-in list of modifiers to parse the filenames better. + + /sys/bus/iio/devices/iio:device0/in_voltage_blue_sample_rate + +In that example, "blue" is a known modifier. "sample" could be the channel\'s name, or part of the attribute name. Note that the channel has no number, which is a pointer that a modifier is used. If the word following "voltage" is not recognized as a modifier, then the attribute is considered not to be a channel attribute, but a device attribute. + +## Reading and writing + +### Enabling channels + +The first thing to do to read or write samples to the hardware, is to enable channels. This can be +done using the public function iio_channel_enable: + +``` c +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); +} +``` + +Note that this function does not perform any operation on the hardware, as it is not a backend +function. Instead, it marks the channel as enabled within its parent iio_device structure. The real +operation of enabling or disabling the channels is performed by the backend, in its "open" function. + +### Creating a buffer + +The second step, is to create a buffer bound to the device that will be used. This can be done with +the public function iio_device_create_buffer: + +``` c +__api struct iio_buffer * iio_device_create_buffer(const struct iio_device *dev, size_t samples_count, bool cyclic); +``` + +- The "dev" parameter corresponds to the iio_device object that will be used. +- The "samples_count" will set the size of the kernel\'s internal buffer. The value set should correspond to the amount of samples that will be asked in each read or write operation. +- The "cyclic" variable is used to inform the kernel whether or not the device is to be opened in cyclic mode. In this configuration, the first buffer of samples pushed to the hardware will be repeated continuously. This will be explained later. + +Internally, the iio_device_create_buffer will call the backend\'s "open" function, defined with the following prototype: + +``` c +struct iio_backend_ops { + int (*open)(const struct iio_device *dev, size_t samples_count, uint32_t *mask, size_t words, bool cyclic); + ... +}; +``` + +The "dev", "samples_count" and "cyclic" parameters are the same as above. The "mask" and "words" parameters are new and shall be explained. The "mask" variable points to an array of 32-bit words. The exact number of words it contains is set in the "words" variable. Each 32-bit word of the array is a bitmask: if the bit Y of the word X is set, then the channel numero (32 \* X + Y) is enabled. Typically, a device doesn\'t have more than a handful of channels, so the "words" variable will almost always be set to 1. But the library must be able to handle even devices with more than 32 channels. Those bits are set or cleared with iio_channel_enable and iio_channel_disable, +respectively; that\'s why it\'s important to call those functions before creating a buffer. + +### Reading and writing, the old way + +In the infancy of the libiio library, there was no iio_buffer class. Instead, there were functions to +open/close and read/write a device: iio_device_open, iio_device_close, iio_device_read, etc. It +worked fine for a start, but was also severely limited, for several reasons: + +- There is no guarantee that the channels that were marked as disabled in the device\'s channel mask can actually be disabled. As a matter of fact, the stream that we read from the hardware can contain samples for channels that we did not request, and when emitting data, the hardware may expect more samples than what we\'re sending. +- The iio_device_read and iio_device_write copy the stream, between the kernel\'s internal buffer and userspace. That is fine for slow devices, not for high-speed analog to digital converters that can produce 30 million samples per second (240 MiB/s worth of data) on a board with a 400 MHz CPU (like the ZedBoard). +- Those read/write functions worked with the same sample format that the hardware manipulates, so the samples had to be converted to a format that can be processed after being copied from the kernel buffer, which resulted in an enormous overhead. + +### Reading and writing with the iio_buffer class + +The iio_buffer object addresses all those issues by providing a smarter API, and encapsulating a +part of the complexity. The iio_buffer object offers several possible methods to read or write +samples: + +- If you just want to dump the content of the internal buffer, you can just use the functions iio_buffer_start and iio_buffer_end to get the start and end addresses of the buffer, and then use the standard memcpy function to dump a part or the totality of the buffer. +- Use iio_buffer_first, iio_buffer_step and iio_buffer_end to iterate over the samples of one given channel contained inside the internal buffer: + ``` c + for (void *ptr = iio_buffer_first(buf, channel); ptr < iio_buffer_end(buf); ptr += iio_buffer_step(buf)) { + /* ptr points to one sample of the channel we're interested in */ + } + ``` + + This method can be very useful if the data of a channel has to be processed sample by sample, as in this case, there is no copy to an intermediate buffer. As the inner loops gets a pointer to the sample\'s emplacement, it can be used either to read or write the buffer. + + + + +- The last method is to use one of the higher-level functions provided by the iio_channel class: iio_channel_read_raw, iio_channel_write_raw, iio_channel_read, iio_channel_write. The former two will basically copy the first N samples of one channel to/from a user-specified buffer (N depending of the size of this one buffer). Note that this function can be replaced with the first method and a memcpy (that\'s what it does internally, after all). The latter two will do the same, but will additionally convert all the samples copied from the raw format to the format that can be used by the applications. + +### Refilling and submitting a buffer + +It has to be noted that all the methods announced above will only work on the samples contained within the internal buffer of the iio_buffer class. In fact, two successive calls to iio_buffer_foreach_sample for instance will iterate over the very same samples twice. To obtain fresh samples from the hardware, it is required to call the function iio_device_refill. Needless to say that the previous content of the buffer is overwritten. On the other hand, to submit samples that were written inside the iio_buffer, it is possible to call the function iio_buffer_push. Those two functions will call either the backend\'s "read" or "write" functions, or if available, its "get_buffer" function (used in the local backend as a high-speed interface. We\'ll see about that later). + +### Format conversion + +As stated previously, the **iio_channel_read** and **iio_channel_write** functions convert the samples from/to their hardware format to/from the format of the architecture on which libiio is running. They actually simply call the iio_channel_convert and iio_channel_convert_inverse public API functions. These two deserve some explanations. + +First, here is a textual representation of the hardware format as reported by the kernel: + + ># cat /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_type + le:s12/16>>4 + +What it says, is that the hardware samples are in little-endian order ("le"), that the sample size is 16 bits, but with only 12 bits worth of data. The "\>\>4" shift informs that those 12 bits are located after 4 most-significant bits (MSB), so in this particular case they correspond to the 12 less-significant bits (LSB), as 16 -- 4 = 12. The "s" character of "s12" means that the 12-bit value is signed. + +From this hardware representation, the conversion functions will process the samples so that they become 16-bit unsigned values. + +The conversion process may look like an easy task; however, the implementation is rather complex, for a good reason: it has been designed to handle samples of any possible size. It makes it possible to process 256-bit samples on a 32-bit CPU, for instance, while compilers typically don\'t handle numbers that large. + +### Low-speed interface + +The low-speed interface of the local backend was the first one to be implemented. It is also the simplest. It is composed by the four following backend functions: + +``` c +struct iio_backend_ops { + int (*open)(const struct iio_device *dev, size_t samples_count, uint32_t *mask, size_t words, bool cyclic); + int (*close)(const struct iio_device *dev); + ssize_t (*read)(const struct iio_device *dev, void *dst, size_t len, uint32_t *mask, size_t words); + ssize_t (*write)(const struct iio_device *dev, const void *src, size_t len); + ... +}; +``` + +The prototype of the "open" function has been explained previously. In the local backend, when the low-speed interface is used, it will perform the following operations: + +- disable the internal kernel buffer (if it was enabled previously), +- set the size of the kernel buffer, by writing the number of samples (given by "samples_count") inside the "buffer/size" attribute of the device (which corresponds to the file "/sys/bus/iio/devices/iio_deviceX/buffer/size"), +- open the "/dev/iio:deviceX" file for reading and writing, +- and finally, enable the channels which should be used by writing their "en" file (e.g. "/sys/bus/iio/devices/iio_deviceX/scan_elements/in_voltage0_en"). + +Then, the "read" and "write" backend functions will respectively read and write the "/dev/iio:deviceX" node. The stream manipulated contains samples in their raw format. Those two functions are used from within the iio_buffer class, when the application requests the buffer to be refilled with iio_buffer_refill or wants to submit a buffer full of data with iio_buffer_push. The data flow occuring with the low-speed interface can be explained with the following schema: + +![lowspeed interface](_static/libiio_lowspeed_interface.png){.align-center width="600"} + +When receiving from an input device (red arrows), the kernel will fill its internal buffer with new +samples, directly from the hardware using DMA transfers if the hardware supports it, or by reading +hardware registers (low-speed devices are generally connected with I2C or SPI). When an +application calls iio_buffer_refill, the samples contained in the kernel\'s buffer will be copied to the +iio_buffer\'s internal buffer, where they can be manipulated. + +When streaming to an output device (blue arrows), the application will fill the iio_buffer\'s +internal buffer with samples then call iio_buffer_push, which will copy those new samples into the +kernel\'s buffer. Then, the kernel will transfer them to the hardware. + +So what exactly makes it a low-speed interface? Between the kernel buffer and the iio_buffer +object, the samples are copied. On slow devices, it doesn\'t matter much. But on devices that can +function at very high frequencies, the overhead caused by the copying process is enormous. As +libiio is meant to be fast even on slow CPUs, a different approach was necessary. + +### High-speed mmap interface + +The high-speed interface is only implemented by the local backend, while the low-speed +interface is also supported by the network backend. It requires a very recent Linux kernel, and even +with that it is not supported by all kernel drivers. In the difference of the low-speed interface, it is +not implemented with the "read" or "write" backend functions, but with a function named +"get_buffer"; as such, one backend can provide both interfaces. This is the case for the local +backend, for instance. + +The "get_buffer", as its name implies, retrieves a buffer from kernel space, or more specifically a +pointer to a buffer allocated in kernel space. The main difference of this interface, is that the kernel +manipulates not only one buffer, but a multitude of buffers. In the local backend, for instance, it +manipulates four buffers. For input channels, the "get_buffer" function will atomically query the +kernel for a new buffer with fresh samples, and push the old buffer back on the kernel\'s buffer +queue. For output channels, the principle stays the same, but the "get_buffer" function will +atomically query an empty buffer that the application can fill, and push the previous buffer that has +been written to. + +Better than a wall of text, this schema sums up the principle of the high-speed interface: + +![Highspeed Interface](_static/libiio_highspeed_interface.png){.align-center width="500" query="?500"} + +Note that this schema represents a capture process. For an output process, just consider all the +arrows inverted. + +Here is the prototype of the "get_buffer" backend function: + +``` c +struct iio_backend_ops { + ssize_t (*get_buffer)(const struct iio_device *dev, void **addr_ptr, size_t bytes_used); + ... +} +``` + +- As expected, the "dev" parameter corresponds to the iio_device object that will be used. +- The "addr_ptr" is a pointer to the address of a buffer. The buffer pointed by "addr_ptr" will be enqueued to the kernel\'s queue, and "addr_pointer" will be changed to point to a new buffer. Note that the interface makes no difference between input and output devices, as in both cases a buffer is enqueued and a new one is dequeued. + +Of course, it\'s always preferable to use the high-speed interface, when the driver allows it. +Internally, the iio_buffer object will first try to use the high-speed interface, and if it\'s detected as +not available, it will switch back to the old interface. + +### Cyclic buffers + +As stated previously, the iio_buffer class allows the creation of cyclic buffers, by setting the "cyclic" parameter of the function iio_device_create_buffer to True. In this case, the very first +buffer pushed (either with the slow interface or the newer high-speed interface) will be repeated +until the buffer is destroyed with iio_buffer_destroy. Once the first buffer has been pushed, any +subsequent call to the function iio_buffer_push will issue an error and return a negative error code. +This feature can be used to output a specific waveform, for instance. + +Note that cyclic buffers only make sense for output devices. The "cyclic" parameter of the +iio_device_create_buffer function will simply be ignored when the related device is a capture +device. + +## The network backend and IIO Daemon + +### Conception of the IIO Daemon + +#### Why a network backend? + +Before libiio even existed, it was already decided that the library had to have a network backend, +and this for several reasons: + +- The first reason is obviously to allow the applications using libiio to stream samples on the network to any connected device. This has some benefits, notably for debugging purposes, and it makes development easier as there is no need to cross-compile the libiio-powered applications anymore. +- Previously, the applications developed at Analog Devices that were using the IIO subsystem were for the most part meant to run directly on the target boards, that are generally equipped with weak CPUs in comparison to regular workstations. While those target do the job when it comes to transferring samples, they are not suitable for processing them, especially at a high speed. It may appear counter-intuitive, but streaming the samples via the network to a more powerful workstation allows to use a much higher sample rate without having samples lost. +- Even when used locally, via the "lo" virtual network interface, a network backend makes sense. While the use of the local backend would result in a higher throughput and less resource usage, the IIO interface of the Linux kernel does not allow for more than one process or thread to access the same device at the same moment. By connecting two clients to the IIO daemon, which serves the requests from the network, it is possible for both of them to receive a copy of the stream of samples. This becomes extremely interesting, as it is now possible for instance to monitor the input stream received by a given application with the IIO Oscillator software (or any other suitable tool). +- Finally, the network backend brings security. For advanced features, the applications using libiio with its local backend require super-user rights, which may not be suitable. When the network backend is used, the same features are available without super-user rights: the applications connect to and dialog with the IIO daemon, which possesses the super-user rights and properly exposes the advanced features to its clients. + +#### Challenges + +Developing the IIO Daemon (IIOD) has been the most challenging part of the project; most of +the complexity of the library is concentrated in this program. To understand why, let\'s see a short list +of the various characteristics of IIOD: + +- It is a network daemon. As such, it has to handle and manage incoming connections. Doing a network daemon properly means handling several clients in parallel, handling client disconnections without crashing, leaking memory or in more general terms leaving the daemon in a bad state. It also has to survive a network failure, disconnect unresponsive clients after a given timeout, etc. +- To each connected client corresponds a thread in the server, that will interpret the commands sent and answer accordingly. All those different threads will execute the very same panel of functions. This means that all those functions must be thread-safe, that is to say safe to use in a multi-thread context: accesses to global resources are protected using locking mechanisms, and avoided as much as possible, in favor of a per-thread context structure. Furthermore, to each opened device correspond one other thread, called in this document the read-write (R/W) thread. For instance, with 4 connected clients opening a different device each, the IIOD has 1+4\*2 = 9 threads running in parallel: one main thread, one client thread per application connected and one R/W thread per device opened. +- It handles multiples clients using the same device at the same time. Each client receives a stream of samples corresponding to the channels requested; the daemon is in charge of duplicating the stream to the connected clients. + +#### Far-distance view + +![libiio Farview](_static/libiio_farview.png){.align-center width="350" query="?350"} + +One interesting characteristic of IIOD is that it actually uses libiio underneath. In a way, it is at +the same time a client application for the libiio library, as well as a component used by the library. +Each command of the interpreter of IIOD is backed by the corresponding API function of libiio; and +in the network backend, to each backend function corresponds one command within the IIOD +server. IIOD has been designed that way to avoid code duplication as much as possible, and to test +the validity and the robustness of the high-level API of libiio. + +### The command interpreter + +#### Flex, Bison + +The command interpreter has been written using the popular GNU tools Flex and Bison, free and +open-source variants of the old Lex and Yacc tools. Those programs can be used to generate a lexer +and a parser from source files defining the commands and the behaviour of the interpreter. The +output consists in C files, that can then be compiled or cross-compiled along with the rest of the +source code of the IIOD server. + +Using Flex and Bison ensured that the code base would stay small, smart and concise. Writing an interpreter from zero in pure C can be a difficult task, and often (if not always) transform in a big code bloat with plenty of space for bugs to hide. This is true especially in this case, where the interpreter is meant to be ran across multiple threads. + +#### Protocol + +In the version 0.1 of the IIOD server, the following commands are accepted: + + HELP + Print this help message + EXIT + Close the current session + PRINT + Display a XML string corresponding to the current IIO context + VERSION + Get the version of libiio in use + TIMEOUT + Set the timeout (in ms) for I/O operations + OPEN [CYCLIC] + Open the specified device with the given mask of channels + CLOSE + Close the specified device + READ DEBUG|[INPUT|OUTPUT ] [] + Read the value of an attribute + WRITE DEBUG|[INPUT|OUTPUT ] [] + Set the value of an attribute + READBUF + Read raw data from the specified device + WRITEBUF + Write raw data to the specified device + GETTRIG + Get the name of the trigger used by the specified device + SETTRIG [] + Set the trigger to use for the specified device + SET BUFFERS_COUNT + Set the number of kernel buffers for the specified device + +When started normally (not in debug mode), the IIOD will answer those commands by a numeric +code sent in plain text. This code corresponds to -22 (the value of -EINVAL) if the parser did not +understand the command. If the command has been properly parsed, the server will forward the +value returned by the underlying libiio function, like iio_device_close for the CLOSE command. + +For some operations, in case the return code is strictly positive, the server will also send a +number of bytes of data defined by the value of the return code. This is the case for instance for the +PRINT, READ and READBUF commands. + +On the other hand, the WRITE and WRITEBUF operations will require a certain number of +bytes to follow the command call; For those operations, this number must be specified as the +"bytes_count" parameter in the command. The client\'s thread will then ignore the given amount of +bytes and just route them to the underlying libiio function. Once all the bytes have been transferred, +the interpreter resumes its normal process. + +### Sending and receiving samples + +#### Opening a device + +Opening a device from the network can be performed with the OPEN command. + + OPEN [CYCLIC] + +The client thread of IIOD that corresponds to the connected client will then register itself as a +potential customer for capturing or uploading samples. + +- If the client application is the first to open the given device, a new thread will be started: the read-write (R/W) thread. Its purpose is to monitor the list of registered clients for R/W requests, read or write the device, and if reading upload the captured data to all the clients requesting samples. +- If the application requests reading and another application is also using the same device for reading, it means that the R/W thread is already started. In this case, the client thread of IIOD will register itself aside of the other application, and the R/W thread will upload the captured data to both. +- If the application requests writing, but another application is already using the same device, an error code is returned. In theory, it should be possible to handle different clients writing to one single device if they are writing to different channels; but that has not been implemented yet. + +As the local backend only allows one device to be opened by one process at a time, the R/W +thread is responsible for holding the iio_buffer object. When started, due to one client using the +OPEN command, it will call iio_device_create_buffer. If all the clients disconnected or used the +CLOSE command, the R/W thread will stop itself after destroying the buffer with +iio_buffer_destroy. + +It is important to note that the iio_buffer object is also destroyed and recreated each time a new +client use the OPEN command. This is done for two reasons: + +- The number of samples requested to libiio must correspond to the highest number requested by the connected clients. This ensures that the buffer used within the IIO subsystem of the Linux kernel is of the right size, so that no samples will be dropped. +- The channel mask requested to libiio must include all the channels requested by the various clients. For instance, if one application sends the OPEN command to use the channel 1, and a separate application sends this command to use the channel 2, then the R/W thread will re-create the iio_buffer object with a channel mask corresponding to the two channels being enabled. This is done to ensure that each client application will receive samples for each channel it requested. + +![](_static/libiio_open_command.png){.align-center width="700" query="?700"} + +#### The read/write thread + +When an application is ready to capture or emit samples, it will send the READBUF or +WRITEBUF command (respectively) to the IIOD server, with the number of bytes to read or write +as parameter: + + READBUF + WRITEBUF + +The corresponding client thread will then mark itself as requesting samples to be transferred, and +then sleep while waiting for a signal from the R/W thread. + +Periodically, the R/W thread will go through its list of registered clients. If the R/W thread +corresponds to an output device, then only one client can register at a time; when this client requests +samples to be written to the device, the R/W thread will copy the stream of samples inside the +iio_buffer object, and then use iio_buffer_push to submit the data to the hardware. + +If one or more clients are requesting to read samples, the thread will call the libiio API function +iio_buffer_refill to fetch a set of fresh samples, and then upload them to each client. + +![](_static/libiio_readbuf_command.png){.align-center width="700" query="?700"} + +As soon as the amount of bytes requested by one client thread has been transferred, it will be +awaken by the R/W thread, and will be passed a return code. + +#### Data transfer + +When one client is registered for writing, it will send the sample data right after the WRITEBUF +command. The number of bytes uploaded to the server must correspond to the number specified in +the WRITEBUF command. When receiving the data, the R/W thread will simply copy it into the +internal buffer of the iio_buffer object then call iio_buffer_push, until all the data has been +submitted to the hardware. + +When one client is registered for reading, as explained previously, the R/W thread will first send +a return code. This value represents an error code if negative, or the amount of bytes that will be +transferred if positive or zero. Right after this value, if no error occurred, the channel mask will be +appended, followed by the data. If the number of bytes specified in the READBUF command is +superior to the size of the buffer, then the data is uploaded in separate chunks, each chunk +containing one return code followed by a number of bytes inferior or equal to the buffer size. This +makes sense, considering that the R/W thread can only refill one buffer worth of data, so it cannot +assume that more than one buffer of samples will be available. + +![](_static/libiio_details_readbuf.png){.align-center width="600" query="?600"} + +A return code of zero is actually not an error code. It is used to notify the client application that +no more samples can be read in this READBUF command, and that a new READBUF command +should be used. This happens for instance when a new client registered, and the channel mask was +modified. + +#### Server-side and client-side de-multiplexing + +When only one client is registered for reading, the process of uploading is rather easy. The R/W +thread will just write the content of the iio_buffer\'s internal buffer to the socket of the client. But +this is different when multiple clients are registered for the same device, and try to access separate +channels; in this case, it is required to de-multiplex the samples. + +The process of de-multiplexing consists in extracting the samples corresponding to a given +channel set, from a stream that contains samples for more channels. In libiio and IIOD, it exists in +two different forms: + +- Server-side de-multiplexing means that the algorithm is executed within the server itself. When uploading the captured data back to the client application, the R/W thread will only upload the samples that belong to channels that were enabled by the application. This has several advantages: the client receives only the samples it requested, and the network link usage is kept relatively low. +- Client-side de-multiplexing means that the algorithm is executed by the client application. On that case, the IIOD server will upload the same buffer to all the clients who requested samples. This has a big negative impact on network usage, as the clients also receive samples of channels that weren\'t requested. Additionally, the applications have to handle the fact that they may obtain less worthy samples than the number they requested.\ + On the plus side, client-side de-multiplexing puts all the burden of the process to the clients, which results in a big win when looking at resource usage of the target board. This is especially true on relatively slow processors, which then become capable of streaming at a much higher throughput without losing samples (around ten times the speed on a 400 MHz board).\ + Furthermore, de-multiplexing is a very easy process for the application, thanks to the iio_buffer functions: iio_buffer_foreach_sample and the combo of iio_buffer_first / iio_buffer_step / iio_buffer_end will simply ignore the samples corresponding to channels that the application didn\'t enable. Provided that the application use one of those methods to read the samples (iio_channel_read / iio_channel_read_raw will work as well), and doesn\'t expect to receive the exact number of samples it requested, then it already supports both server-side and client-side de-multiplexing. + +### The network backend + +#### Sending and receiving commands + +In comparison with the complexity of the IIOD server, the network backend of the library is +extremely simple. Each call to one of its backend function will result in a command being sent to +the IIOD server. The "open" backend function will send the OPEN command, the "read" function +will send READBUF, etc. The parameters passed to the backend functions are properly converted to +ASCII according to the schema that the IIOD understands. The code returned by the server will be +used as the return value of the backend function. + +![](_static/libiio_commands.png){.align-center width="500" query="?500"} + +Considering that the IIOD also uses the public API of libiio, the network backend is completely +transparent: the backend functions behave just like their local backend counterpart. In theory, it +would be completely possible to make IIOD use the network backend, and chain two IIOD servers together! + +#### Context creation + +One interesting note about the network backend, is how it creates its context. Once the +application calls the API function iio_create_network_context, and the network link is established +with the IIOD server, the initialization function of the network backend sends the PRINT command +to the server. For the record, this command returns an XML string which represents the structure of +the whole IIO context as seen by the server. From that XML string, the network backend will +actually create a IIO context from the XML backend, using the public API function +iio_context_create_xml_mem. Then, it will just piggy-back its own set of backend functions to +replace the ones provided by the XML backend. + +#### Zero-configuration + +One late feature implemented in the network backend of libiio and in the IIOD server has been +the support of auto-configuration. If a NULL pointer is passed as the "hostname" argument of +iio_create_network_context, then the network backend will attempt to discover and establish a +connection to a IIOD server present on the network. + +This feature depends on the Avahi zero-configuration software stack. It can be enabled by setting +the preprocessor flag HAVE_AVAHI to a non-zero value; the IIOD will then register itself to the +Avahi daemon if one is running on the target, and the network backend will query the local Avahi +server for a IIOD server to use. + +## libiio, now and in the future + +### Bindings + +C is a good language, but not everybody is familiar with it; some people might prefer object-oriented languages, like Java or Python for instance. This is why we created bindings for the library, so that it can be used with different programming languages. The additional languages supported by libiio are now Python and C#. + +\Since v0.19 all bindings require explicit flags to be enabled during builds. Please reference the [build instructions](https://github.com/analogdevicesinc/libiio/blob/master/README_BUILD.md) to make sure the desired bindings are built and installed.\ + +#### Python bindings + +The Python bindings were developed very early in the project to facilitate generating XML strings modeling IIO contexts in order to properly test the XML backend. They quickly became outdated as the project moved on, but were later greatly improved and should now be on par with the C# bindings. + +To create the bindings, The "ctypes" module has been used: + +``` python +from ctypes import POINTER, Structure, cdll, c_char_p + +def _init(): + class _Device(Structure): + pass + + DevicePtr = POINTER(_Device) + + lib = cdll.LoadLibrary('libiio.so.0') + + global _d_get_id + _d_get_id = lib.iio_device_get_id + _d_get_id.restype = c_char_p + _d_get_id.archtypes = (DevicePtr, ) + +class Device(object): + def __init__(self, _device): + self._device = _device + self.id = _d_get_id(self._device) +``` + +This code shows the \_init function, whose goal here is to load the library and create wrappers in +Python for the C functions of libiio. Objects created from the Device class provide a couple of +methods that will call those wrappers. Some of the known constant values of the IIO objects, like +the ID of a device, are cached within the Python classes for faster access. Then, printing the ID of a +device in a Python script is as easy as typing "print my_device.id". To better understand the available calls consult the [bindings source](https://github.com/analogdevicesinc/libiio/blob/master/bindings/python/iio.py) and some available [examples](https://github.com/analogdevicesinc/plutosdr_scripts). + +##### Installation + +Since v0.21 the python bindings have been available through pypi, and therefore can be installed with pip: + + pip install pylibiio + +\Note that this will just install the bindings and will error if the library itself is not installed.\ + +##### PyADI-IIO + +An additional module was created which leverages the libiio python bindings call [pyadi-iio](https://analogdevicesinc.github.io/pyadi-iio/). pyadi-iio is recommended if a device specific class exists for your [current hardware](https://github.com/analogdevicesinc/pyadi-iio/blob/master/supported_parts.md). However, since pyadi-iio uses libiio all the libiio python APIs are available in that module if needed. + +#### C# bindings + +The C# bindings in particular are fully functional and cover the whole panel of features that +libiio provides. Its API consists in the Context, Device, Channel and IOBuffer classes, each one +providing a couple of methods, that directly call their C counterpart: + +``` csharp +namespace iio +{ + public class Device + { + public IntPtr dev; + ... + + [DllImport("libiio.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr iio_device_get_id(IntPtr dev); + + public string id() + { + return Marshal.PtrToStringAnsi(iio_device_get_id(dev)); + } + } +} +``` + +In this example, if you call the id method on an object of type Device, as a result the C function +iio_device_get_id will be called. All the others methods implement a similar mechanism. + +The C# bindings are especially interesting for Windows users, because they permit to use libiio +in a .NET application (with the network backend, of course). By using Mono, it is also possible to +use libiio in C# programs running on Linux. + +### Future improvements + +#### Zerocopy + +The current version of the IIOD server is extremely fast, provided that client-side demultiplexing is used. On a weak 400 MHz CPU, it has no problems to stream samples at speeds of 3MSPS (3 million samples per second) without dropping a single one. + +However, 3 MSPS is really far from the maximum capacity of the typical converters. The [AD-FMCOMMS3-EBZ](https://wiki.analog.com/resources/eval/user-guides/ad-fmcomms3-ebz/) board, for instance, can digitize at a speed up to 61.44 MSPS (time 4 channels, for a total of 245.76 MSPS) \... nearly 100 times faster. And the recent [AD-FMCDAQ2-EBZ](https://wiki.analog.com/resources/eval/user-guides/ad-fmcdaq2-ebz/) is capable of 1000 MSPS for dual 16-bit channels, which +represents a maximum transfer speed of 2000 MSPS, or 32.0 Gb/s. + +So how to reach those extremely high speeds, with a 400 MHz CPU? That\'s where [Zero copy](https://en.wikipedia.org/wiki/Zero-copy) +comes in play. To understand what zerocopy is, and how useful it can be, let\'s quickly recall how the +IIOD uploads the samples from one device to a connected client: + +- When the low-speed interface is used, the samples are acquired from the hardware using DMA, and stored into a kernel buffer; the local backend of libiio will then use the CPU to copy the samples to the iio_buffer object, and then copy again to the packet buffer of the Linux kernel by writing the client\'s socket.\ + ![](_static/libiio_route_lowspeed.png){.align-center width="600" query="?600"} + + + +- Then comes zerocopy. The principle, is to avoid the second manual copy of the samples, when the data is written to the socket. So how can this work? How is it possible to send data through the network, without writing the socket?\ + The answer is to be found deep inside the Linux kernel. A particular system call, named "[vmsplice](https://en.wikipedia.org/wiki/Splice_(system_call))", allows to "give" virtual pages of memory to the Linux kernel through a file descriptor. This trick, introduced in Linux 2.6.17, works by re-configuring the Memory Management Unit of the CPU. Let\'s say the buffer to write to the socket starts at address 0x4000; the vmsplice system call will map the exact same area to a different address in kernel space, e.g. 0x8000, so that the buffer can be addressed from both addresses. Then, it will re-map the original 0x4000 address to point to a different physical location. As a result, a complete buffer of samples has been moved to kernel space, without copying a single byte of it! Then, from the hardware to the network link, the CPU never has to copy anything, and the 400 MHz ARM board can push hundreds of megabytes of samples on the network while having a CPU usage staying close to zero.\ + ![](_static/libiio_route_zerocopy.png){.align-center width="600" query="?600"}\ + While vmsplice moves the pages to kernel space, it is also possible to duplicate them using the "tee" system call, and serve the same data to multiple clients connected to the IIOD server without using the CPU at all once again.\ + This very interesting feature of the kernel doesn\'t come without drawbacks: the address and the size of the buffer must be aligned to the page size for it to work. It is also uncertain how the IIO drivers within the Linux kernel would cope with this feature; but that is certainly something that will be attempted in the future. diff --git a/doc/source/tools/iio_attr.rst b/doc/source/tools/iio_attr.rst new file mode 100644 index 000000000..84fcc161c --- /dev/null +++ b/doc/source/tools/iio_attr.rst @@ -0,0 +1,148 @@ +iio_attr +======== + +iio_attr - list IIO devices, and read/write device attributes + +SYNOPSIS +-------- + +| **iio_attr** [ *options* ] -d [device] [attr] [value] +| **iio_attr** [ *options* ] -c [device] [channel] [attr] [value] +| **iio_attr** [ *options* ] -D [device] [attr] [value] +| **iio_attr** [ *options* ] -C [attr] +| **iio_attr** -S +| **iio_attr** -h + +DESCRIPTION +----------- + +**iio_attr** is a utility for displaying information about local or +remote IIO devices. By providing an optional value, **iio_attr** will +attempt to write the new value to the attribute. + +COMMANDS +-------- + +The iio_attr utility has a few main options, which control what the main utility of the application is. + +**-d, --device-attr** + Read and Write IIO device attributes + +**-c --channel-attr** + Read and Write IIO channel attributes + +**-B --buffer-attr** + Read and Write IIO Buffer attributes + +**-C --context-attr** + Read and Write IIO Context attributes + +**-D --debug-attr** + Read and Write IIO Debug attributes + +**-h, --help** + Tells *iio_attr* to display some help, and then quit. + +**-V, --version** + Prints the version information for this particular copy of *iio_attr* + and the version of the libiio library it is using. This is useful for + knowing if the version of the library and *iio_attr* on your system + are up to date. This is also useful when reporting bugs. + +**-S, --scan [backends]** + Scan for available IIO contexts, optional arg of specific backend(s) + 'ip', 'usb' or 'ip,usb'. Specific options for USB include Vendor ID, + Product ID to limit scanning to specific devices 'usb=0456:b673'. + vid,pid are hexadecimal numbers (no prefix needed), "\*" (match any + for pid only) If no argument is given, it checks all that are + available. + +OPTIONS +------- + +**-u, --uri** + The Uniform Resource Identifier *(uri)* for connecting to devices, + can be one of: + + ip:[address] + network address, either numeric (192.168.0.1) or network hostname + + ip: + blank, if compiled with zeroconf support, will find an IIO device + on network + + usb:[device:port:instance] + normally returned from **iio_attr -S** + + serial:[port],[baud],[settings] + which are controlled, and need to match the iiod (or tinyiiod) on + the other end of the serial port. + + [port] + is something like '/dev/ttyUSB0' on Linux, and 'COM4' on + Windows. + + [baud] + is is normally one of 110, 300, 600, 1200, 2400, 4800, 9600, + 14400, 19200, 38400, 57600, 115200 [default], 128000 or 256000, + but can vary system to system. + + [settings] + would normally be configured as '8n1' this is controlled by: + + data_bits: + (5, 6, 7, 8 [default], or 9) + + parity_bits: + ('n' none [default], 'o' odd, 'e' even, 'm' mark, or 's' + space) + + stop_bits: + (1 [default, or 2) + + flow_control: + ('0' none [default], 'x' Xon Xoff, 'r' RTSCTS, or 'd' + DTRDSR) + + local: + with no address part. + +**-i, --input-channel** + Filters channels by input channels only + +**-o, --output-channel** + Filters channels by output channels only + +**-s, --scan-channel** + Filters channels by scan channels only + +**-I, --ignore-case** + When pattern matching devices, channels or attributes, ignore case + +**-g, --generate-code ** + Generate small C or python snippets that emulate what you are doing + on the command line. Argument is a file name 'foo.c' or 'foo.py' + +RETURN VALUE +------------ + +If the specified device is not found, a non-zero exit code is returned. + +SEE ALSO +-------- + +**iio_attr**\ (1), **iio_info**\ (1), **iio_rwdev**\ (1), +**iio_reg**\ (1), **libiio**\ (3) + +libiio home page: +**https://wiki.analog.com/resources/tools-software/linux-software/libiio** + +libiio code: **https://github.com/analogdevicesinc/libiio** + +Doxygen for libiio **https://analogdevicesinc.github.io/libiio/** + +BUGS +---- + +All bugs are tracked at: +**https://github.com/analogdevicesinc/libiio/issues** diff --git a/doc/source/tools/iio_genxml.rst b/doc/source/tools/iio_genxml.rst new file mode 100644 index 000000000..317493bf2 --- /dev/null +++ b/doc/source/tools/iio_genxml.rst @@ -0,0 +1,107 @@ +iio_genxml +========== + +iio_genxml - Generate XML representation of a libiio context + +SYNOPSIS +-------- + +**iio_genxml** [ *options* ] + +DESCRIPTION +----------- + +**iio_genxml** is a utility for generating a XML representation of a +libiio context. + +OPTIONS +------- + +**-h, --help** + Tells *iio_genxml* to display some help, and then quit. + +**-V, --version** + Prints the version information for this particular copy of + *iio_genxml* and the version of the libiio library it is using. This + is useful for knowing if the version of the library and *iio_genxml* + on your system are up to date. This is also useful when reporting + bugs. + +**-S, --scan [backends]** + Scan for available IIO contexts, optional arg of specific backend(s) + 'ip', 'usb' or 'ip,usb'. Specific options for USB include Vendor ID, + Product ID to limit scanning to specific devices 'usb=0456:b673'. + vid,pid are hexadecimal numbers (no prefix needed), "\*" (match any + for pid only) If no argument is given, it checks all that are + available. + +**-u, --uri** + The Uniform Resource Identifier *(uri)* for connecting to devices, + can be one of: + + ip:[address] + network address, either numeric (192.168.0.1) or network hostname + + ip: + blank, if compiled with zeroconf support, will find an IIO device + on network + + usb:[device:port:instance] + normally returned from **iio_genxml -S** + + serial:[port],[baud],[settings] + which are controlled, and need to match the iiod (or tinyiiod) on + the other end of the serial port. + + [port] + is something like '/dev/ttyUSB0' on Linux, and 'COM4' on + Windows. + + [baud] + is is normally one of 110, 300, 600, 1200, 2400, 4800, 9600, + 14400, 19200, 38400, 57600, 115200 [default], 128000 or 256000, + but can vary system to system. + + [settings] + would normally be configured as '8n1' this is controlled by: + + data_bits: + (5, 6, 7, 8 [default], or 9) + + parity_bits: + ('n' none [default], 'o' odd, 'e' even, 'm' mark, or 's' + space) + + stop_bits: + (1 [default, or 2) + + flow_control: + ('0' none [default], 'x' Xon Xoff, 'r' RTSCTS, or 'd' + DTRDSR) + + local: + with no address part. + +RETURN VALUE +------------ + +If the specified device is not found, a non-zero exit code is returned. + +SEE ALSO +-------- + +**iio_attr**\ (1), **iio_info**\ (1), **iio_rwdev**\ (1), +**iio_reg**\ (1), **libiio**\ (3) + +libiio home page: +**https://wiki.analog.com/resources/tools-software/linux-software/libiio** + +libiio code: **https://github.com/analogdevicesinc/libiio** + +Doxygen for libiio **https://analogdevicesinc.github.io/libiio/** + +BUGS +---- + +All bugs are tracked at: +**https://github.com/analogdevicesinc/libiio/issues** diff --git a/doc/source/tools/iio_info.rst b/doc/source/tools/iio_info.rst new file mode 100644 index 000000000..59529a8e6 --- /dev/null +++ b/doc/source/tools/iio_info.rst @@ -0,0 +1,109 @@ +iio_info +======== + +iio_info - list IIO devices and device attributes + +SYNOPSIS +-------- + +**iio_info** [ *options* ] -u + +DESCRIPTION +----------- + +**iio_info** is a utility for displaying information about local or +remote IIO devices + +OPTIONS +------- + +**-h, --help** + Tells *iio_info* to display some help, and then quit. + +**-V, --version** + Prints the version information for this particular copy of *iio_info* + and the version of the libiio library it is using. This is useful for + knowing if the version of the library and *iio_info* on your system + are up to date. This is also useful when reporting bugs. + +**-S, --scan [backends]** + Scan for available IIO contexts, optional arg of specific backend(s) + 'ip', 'usb' or 'ip,usb'. Specific options for USB include Vendor ID, + Product ID to limit scanning to specific devices 'usb=0456:b673'. + vid,pid are hexadecimal numbers (no prefix needed), "\*" (match any + for pid only) If no argument is given, it checks all that are + available. + +**-u, --uri** + The Uniform Resource Identifier *(uri)* for connecting to devices, + can be one of: + + ip:[address] + network address, either numeric (192.168.0.1) or network hostname + + ip: + blank, if compiled with zeroconf support, will find an IIO device + on network + + usb:[device:port:instance] + normally returned from **iio_info -S** + + serial:[port],[baud],[settings] + which are controlled, and need to match the iiod (or tinyiiod) on + the other end of the serial port. + + [port] + is something like '/dev/ttyUSB0' on Linux, and 'COM4' on + Windows. + + [baud] + is is normally one of 110, 300, 600, 1200, 2400, 4800, 9600, + 14400, 19200, 38400, 57600, 115200 [default], 128000 or 256000, + but can vary system to system. + + [settings] + would normally be configured as '8n1' this is controlled by: + + data_bits: + (5, 6, 7, 8 [default], or 9) + + parity_bits: + ('n' none [default], 'o' odd, 'e' even, 'm' mark, or 's' + space) + + stop_bits: + (1 [default, or 2) + + flow_control: + ('0' none [default], 'x' Xon Xoff, 'r' RTSCTS, or 'd' + DTRDSR) + + local: + with no address part. + +**-a, --auto** + Scan for available contexts and if only one is available use it. + +RETURN VALUE +------------ + +If the specified device is not found, a non-zero exit code is returned. + +SEE ALSO +-------- + +**iio_attr**\ (1), **iio_info**\ (1), **iio_rwdev**\ (1), +**iio_reg**\ (1), **libiio**\ (3) + +libiio home page: +**https://wiki.analog.com/resources/tools-software/linux-software/libiio** + +libiio code: **https://github.com/analogdevicesinc/libiio** + +Doxygen for libiio **https://analogdevicesinc.github.io/libiio/** + +BUGS +---- + +All bugs are tracked at: +**https://github.com/analogdevicesinc/libiio/issues** diff --git a/doc/source/tools/iio_reg.rst b/doc/source/tools/iio_reg.rst new file mode 100644 index 000000000..84d4bb598 --- /dev/null +++ b/doc/source/tools/iio_reg.rst @@ -0,0 +1,111 @@ +iio_reg +======= + +iio_reg - do a low level read or write to SPI or I2C register + +SYNOPSIS +-------- + +**iio_reg** [ *options* ] [] + +DESCRIPTION +----------- + +**iio_reg** is a utility for debugging local or remote IIO devices. It +should not be used by normal users, and is normally used by driver +developers during development, or by end users debugging a driver, or +sending in a feature request. It provides a mechanism to read or write +SPI or I2C registers for IIO devices. This can be useful when +troubleshooting IIO devices, and understanding how the Linux IIO +subsystem is managing the device. + +OPTIONS +------- + +**-h, --help** + Tells *iio_reg* to display some help, and then quit. + +**-V, --version** + Prints the version information for this particular copy of *iio_reg* + and the version of the libiio library it is using. This is useful for + knowing if the version of the library and *iio_reg* on your system + are up to date. This is also useful when reporting bugs. + +**-S, --scan [backends]** + Scan for available IIO contexts, optional arg of specific backend(s) + 'ip', 'usb' or 'ip,usb'. Specific options for USB include Vendor ID, + Product ID to limit scanning to specific devices 'usb=0456:b673'. + vid,pid are hexadecimal numbers (no prefix needed), "\*" (match any + for pid only) If no argument is given, it checks all that are + available. + +**-u, --uri** + The Uniform Resource Identifier *(uri)* for connecting to devices, + can be one of: + + ip:[address] + network address, either numeric (192.168.0.1) or network hostname + + ip: + blank, if compiled with zeroconf support, will find an IIO device + on network + + usb:[device:port:instance] + normally returned from **iio_reg -S** + + serial:[port],[baud],[settings] + which are controlled, and need to match the iiod (or tinyiiod) on + the other end of the serial port. + + [port] + is something like '/dev/ttyUSB0' on Linux, and 'COM4' on + Windows. + + [baud] + is is normally one of 110, 300, 600, 1200, 2400, 4800, 9600, + 14400, 19200, 38400, 57600, 115200 [default], 128000 or 256000, + but can vary system to system. + + [settings] + would normally be configured as '8n1' this is controlled by: + + data_bits: + (5, 6, 7, 8 [default], or 9) + + parity_bits: + ('n' none [default], 'o' odd, 'e' even, 'm' mark, or 's' + space) + + stop_bits: + (1 [default, or 2) + + flow_control: + ('0' none [default], 'x' Xon Xoff, 'r' RTSCTS, or 'd' + DTRDSR) + + local: + with no address part. + +RETURN VALUE +------------ + +If the specified device is not found, a non-zero exit code is returned. + +SEE ALSO +-------- + +**iio_attr**\ (1), **iio_info**\ (1), **iio_rwdev**\ (1), +**iio_reg**\ (1), **libiio**\ (3) + +libiio home page: +**https://wiki.analog.com/resources/tools-software/linux-software/libiio** + +libiio code: **https://github.com/analogdevicesinc/libiio** + +Doxygen for libiio **https://analogdevicesinc.github.io/libiio/** + +BUGS +---- + +All bugs are tracked at: +**https://github.com/analogdevicesinc/libiio/issues** diff --git a/doc/source/tools/iio_rwdev.rst b/doc/source/tools/iio_rwdev.rst new file mode 100644 index 000000000..9b497c839 --- /dev/null +++ b/doc/source/tools/iio_rwdev.rst @@ -0,0 +1,159 @@ +iio_rwdev +========= + +iio_rwdev - read/write buffers from/to an IIO device + +SYNOPSIS +-------- + +**iio_rwdev** [ *options* ] [-n ] [-t ] [-T +] [-b ] [-s ] [-w] +[ ...] + +DESCRIPTION +----------- + +**iio_reg** is a utility for reading buffers from connected IIO devices, +and sending results to standard out. + +OPTIONS +------- + +**-h, --help** + Tells *iio_rwdev* to display some help, and then quit. + +**-V, --version** + Prints the version information for this particular copy of + *iio_rwdev* and the version of the libiio library it is using. This + is useful for knowing if the version of the library and *iio_rwdev* + on your system are up to date. This is also useful when reporting + bugs. + +**-S, --scan [backends]** + Scan for available IIO contexts, optional arg of specific backend(s) + 'ip', 'usb' or 'ip,usb'. Specific options for USB include Vendor ID, + Product ID to limit scanning to specific devices 'usb=0456:b673'. + vid,pid are hexadecimal numbers (no prefix needed), "\*" (match any + for pid only) If no argument is given, it checks all that are + available. + +**-w --write** + Write sample data to the IIO device. The default is to read samples + from it. + +**-t --trigger** + Use the specified trigger, if needed on the specified channel + +**-b --buffer-size** + Size of the capture buffer. Default is 256. + +**-s --samples** + Number of samples (not bytes) to capture, 0 = infinite. Default is 0. + +**-T --timeout** + Buffer timeout in milliseconds. 0 = no timeout. Default is 0. + +**-u, --uri** + The Uniform Resource Identifier *(uri)* for connecting to devices, + can be one of: + + ip:[address] + network address, either numeric (192.168.0.1) or network hostname + + ip: + blank, if compiled with zeroconf support, will find an IIO device + on network + + usb:[device:port:instance] + normally returned from **iio_rwdev -S** + + serial:[port],[baud],[settings] + which are controlled, and need to match the iiod (or tinyiiod) on + the other end of the serial port. + + [port] + is something like '/dev/ttyUSB0' on Linux, and 'COM4' on + Windows. + + [baud] + is is normally one of 110, 300, 600, 1200, 2400, 4800, 9600, + 14400, 19200, 38400, 57600, 115200 [default], 128000 or 256000, + but can vary system to system. + + [settings] + would normally be configured as '8n1' this is controlled by: + + data_bits: + (5, 6, 7, 8 [default], or 9) + + parity_bits: + ('n' none [default], 'o' odd, 'e' even, 'm' mark, or 's' + space) + + stop_bits: + (1 [default, or 2) + + flow_control: + ('0' none [default], 'x' Xon Xoff, 'r' RTSCTS, or 'd' + DTRDSR) + + local: + with no address part. + +RETURN VALUE +------------ + +If the specified device is not found, a non-zero exit code is returned. + +USAGE +----- + +You use iio_rwdev in the same way you use many of the other libiio +utilities. You should specify a IIO device, and the specific channel to +read or write. When reading, channels must be input; when writing they +must be output. If no channel is provided, iio_rwdev will read from all +input channels or write to all output channels. If no device is +provided, iio_rwdev will print a few examples: + + | **``iio_rwdev`` -a** + | Using auto-detected IIO context at URI "usb:3.10.5" + | Example : iio_rwdev -u usb:3.10.5 -b 256 -s 1024 cf-ad9361-lpc + voltage0 + | Example : iio_rwdev -u usb:3.10.5 -b 256 -s 1024 cf-ad9361-lpc + voltage1 + | Example : iio_rwdev -u usb:3.10.5 -b 256 -s 1024 cf-ad9361-lpc + voltage2 + | Example : iio_rwdev -u usb:3.10.5 -b 256 -s 1024 cf-ad9361-lpc + voltage3 + | Example : iio_rwdev -u usb:3.10.5 -b 256 -s 1024 cf-ad9361-lpc + +This captures 1024 samples of I and Q data from the USB attached AD9361, +and stores it (as raw binary) into the file samples.dat + + **``iio_rwdev`` -a -s 1024 cf-ad9361-lpc voltage0 voltage1 > + samples.dat** + +And plots the data with gnuplot. + + **``gnuplot`` -e "set term png; set output 'sample.png'; plot + 'sample.dat' binary format='%short%short' using 1 with lines, + 'sample.dat' binary format='%short%short' using 2 with lines;"** + +SEE ALSO +-------- + +**iio_attr**\ (1), **iio_info**\ (1), **iio_rwdev**\ (1), +**iio_reg**\ (1), **libiio**\ (3) + +libiio home page: +**https://wiki.analog.com/resources/tools-software/linux-software/libiio** + +libiio code: **https://github.com/analogdevicesinc/libiio** + +Doxygen for libiio **https://analogdevicesinc.github.io/libiio/** + +BUGS +---- + +All bugs are tracked at: +**https://github.com/analogdevicesinc/libiio/issues** diff --git a/doc/source/tools/iio_stresstest.rst b/doc/source/tools/iio_stresstest.rst new file mode 100644 index 000000000..89305e8d5 --- /dev/null +++ b/doc/source/tools/iio_stresstest.rst @@ -0,0 +1,128 @@ +iio_stresstest +============== + +iio_stresstest - Stress test program for libiio + +SYNOPSIS +-------- + +**iio_stresstest** [ *options* ] -u + +DESCRIPTION +----------- + +**iio_stresstest** is a stress-testing program that can be used to find +bugs in libiio or in the libiio daemon (IIOD). + +OPTIONS +------- + +**-h, --help** + Tells *iio_stresstest* to display some help, and then quit. + +**-V, --version** + Prints the version information for this particular copy of + *iio_stresstest* and the version of the libiio library it is using. + This is useful for knowing if the version of the library and + *iio_stresstest* on your system are up to date. This is also useful + when reporting bugs. + +**-S, --scan [backends]** + Scan for available IIO contexts, optional arg of specific backend(s) + 'ip', 'usb' or 'ip,usb'. Specific options for USB include Vendor ID, + Product ID to limit scanning to specific devices 'usb=0456:b673'. + vid,pid are hexadecimal numbers (no prefix needed), "\*" (match any + for pid only) If no argument is given, it checks all that are + available. + +**-u, --uri** + The Uniform Resource Identifier *(uri)* for connecting to devices, + can be one of: + + ip:[address] + network address, either numeric (192.168.0.1) or network hostname + + ip: + blank, if compiled with zeroconf support, will find an IIO device + on network + + usb:[device:port:instance] + normally returned from **iio_stresstest -S** + + serial:[port],[baud],[settings] + which are controlled, and need to match the iiod (or tinyiiod) on + the other end of the serial port. + + [port] + is something like '/dev/ttyUSB0' on Linux, and 'COM4' on + Windows. + + [baud] + is is normally one of 110, 300, 600, 1200, 2400, 4800, 9600, + 14400, 19200, 38400, 57600, 115200 [default], 128000 or 256000, + but can vary system to system. + + [settings] + would normally be configured as '8n1' this is controlled by: + + data_bits: + (5, 6, 7, 8 [default], or 9) + + parity_bits: + ('n' none [default], 'o' odd, 'e' even, 'm' mark, or 's' + space) + + stop_bits: + (1 [default, or 2) + + flow_control: + ('0' none [default], 'x' Xon Xoff, 'r' RTSCTS, or 'd' + DTRDSR) + + local: + with no address part. + +**-a, --auto** + Scan for available contexts and if only one is available use it. + +**-T, --timeout ** + Context timeout in milliseconds. 0 = no timeout (wait forever) + +**-b, --buffer-size ** + Size of the capture buffer. Default is 256. + +**-s, --samples ** + Number of samples to capture, 0 = infinite. Default is 0. + +**-d, --duration ** + Time to wait (in seconds) before stopping all threads + +**-t, --threads ** + Number of threads to use + +**-v, --verbose** + Increase verbosity (-vv and -vvv for more) + +RETURN VALUE +------------ + +If the specified device is not found, a non-zero exit code is returned. + +SEE ALSO +-------- + +**iio_attr**\ (1), **iio_info**\ (1), **iio_rwdev**\ (1), +**iio_reg**\ (1), **libiio**\ (3) + +libiio home page: +**https://wiki.analog.com/resources/tools-software/linux-software/libiio** + +libiio code: **https://github.com/analogdevicesinc/libiio** + +Doxygen for libiio **https://analogdevicesinc.github.io/libiio/** + +BUGS +---- + +All bugs are tracked at: +**https://github.com/analogdevicesinc/libiio/issues** diff --git a/doc/source/tools/index.rst b/doc/source/tools/index.rst new file mode 100644 index 000000000..7c84cc77e --- /dev/null +++ b/doc/source/tools/index.rst @@ -0,0 +1,16 @@ +Command Line Tools +================== + +libiio provides a set of command line tools to interact with libiio contexts. These tools are useful for debugging, testing, and development. They are typically referred to as the iio-tools or iio-utils. + +.. toctree:: + :maxdepth: 1 + + iio_attr + iio_info + iio_genxml + iio_rwdev + iio_reg + iio_stresstest + + diff --git a/doc/source/training.md b/doc/source/training.md new file mode 100644 index 000000000..32aa3e281 --- /dev/null +++ b/doc/source/training.md @@ -0,0 +1,10 @@ +# Training Material + +libiio has existed for many years and numerous training materials have been created. Here are some of the most popular: + +## Videos + +- [Using the Linux IIO framework for SDR](https://av.tib.eu/media/34480) +- [ADALM-PLUTO and IIO Workshop](https://www.youtube.com/watch?v=Yx3RPOtv7x8) +- [Using the Industrial I/O Framework for Easy Prototyping of Software Defined Radio (SDR) Transceivers](https://www.youtube.com/watch?v=8WaVkb5osRs) +- [libiio - A Library for Interfacing with Linux IIO Devices - Dan Nechita, Analog Devices Inc](https://www.youtube.com/watch?v=p_VntEwUe24) \ No newline at end of file diff --git a/doc/source/usage.md b/doc/source/usage.md new file mode 100644 index 000000000..a82d5d9af --- /dev/null +++ b/doc/source/usage.md @@ -0,0 +1,268 @@ +# Usage Model + +The basic bricks of the libiio API are the iio_context, iio_device, iio_channel and iio_buffer classes. + +![Caption text](_static/codemodel.svg) + +- A iio_context object may contain zero or more iio_device objects. + A iio_device object is associated with only one iio_context. + This object represents an instance of the library. +- A iio_device object may contain zero or more iio_channel objects. A iio_channel object is associated with only one iio_device. +- A iio_device object may be associated with one iio_buffer object, and a iio_buffer object is associated with only one iio_device. + +:::{note} +A C++ API is provided in the form of a wrapper around the C API. The C++ API is not a one-to-one mapping of the C API, but rather a object oriented approach to the C API with the use of classes and methods. +::: + +## Scanning for IIO contexts +The first step when dealing with a collection of IIO devices (known as a context) is to find the context. This can be connected via usb, network, serial or local. Having these different connectivity options could prove to be problematic, but libiio abstracts the low level communications away, and allows you just to find contexts, and talk to devices without being interested in the low level aspects. Many find this convenient to develop applications and algorithms on a host, and quickly move to an embedded Linux system without having to change any code. + +To find what IIO contexts are available, use the following: +- iio_create_scan_context(): Create a iio_scan_context object. Depending on what backends were enabled with compiling the library, some of them may not be available. The first argument to this function is a string which is used as a filter ("usb:", "ip:", "local:", "usb:ip", where the default (NULL) means any backend that is compiled in). +- iio_scan_context_get_info_list(): get the iio_context_info object from the iio_scan_context object. The iio_context_info object can be examined with the iio_context_info_get_description() and iio_context_info_get_uri() to determine which uri you want to attach to. + +## Creating a context +Different functions are available to create the iio_context object. Depending on what backends were enabled when compiling the library, some of them may not be available. +Each function will result in a different backend being used. + +Those functions are: +- iio_create_context_from_uri(): This should be the main function to create contexts, which takes a Uniform Resource Identifier (uri), and returns a iio_context. +- iio_create_default_context(): Create a "local" context if we can, otherwise use the ENV_VAR IIOD_REMOTE. +- iio_create_local_context(): Create a "local" context, to use the IIO devices connected to the system (typically for cross-compiled applications). +- iio_create_network_context(): Create a "network" context that will work with a remotely connected target. + +Note that every function that compose the API of libiio will work independently of the function that was used to create the iio_context object. + +The iio_context object can later be destroyed with iio_context_destroy(). + +### Navigation +#### Device objects +- From the iio_context object, you can obtain the number of available devices with iio_context_get_devices_count(). +- Then, each iio_device object can be accessed with iio_context_get_device(). +- Alternatively, it is possible to lookup for a device name or ID with iio_context_find_device(). + +Each iio_device object has an ID that can be used as identifier. This ID can be retrieved with iio_device_get_id(). +It optionally also has a name, that can be retrieved with iio_device_get_name(). + +#### Channel objects +- From the iio_device object, you can obtain the number of available channels with iio_device_get_channels_count(). +- Then, each iio_channel object can be accessed with iio_device_get_channel(). +- Alternatively, it is possible to lookup for a channel name or ID with iio_device_find_channel(). + +Each iio_channel can be either input, or output. This information can be retrieved with iio_channel_is_output(). +As for the Device objects, the iio_channel object features an ID and optionally a name. +The ID can be obtained with iio_channel_get_id(), and the name can be obtained with iio_channel_get_name(). +Important note: two iio_channel can have the same ID, as long as one is input and the other is output. + +## Parameters +Different kinds of parameters are available: parameters that apply to a iio_device, and parameters that apply to one or more iio_channel. +- The number of device-specific parameters can be obtained with iio_device_get_attrs_count(). Each attribute name can be obtained with iio_device_get_attr(). +- The number of channel-specific attributes can be obtained with iio_channel_get_attrs_count(). Each attribute name can be obtained with iio_channel_get_attr(). + +Alternatively, it is possible to lookup for the name of an attribute with iio_device_find_attr() and iio_channel_find_attr(). + +## Reading and modifying parameters + +### Reading a parameter +Read device-specific attributes with those functions: +- iio_device_attr_read() +- iio_device_attr_read_all() +- iio_device_attr_read_bool() +- iio_device_attr_read_longlong() +- iio_device_attr_read_double() + +Read channel-specific attributes with those functions: +- iio_channel_attr_read() +- iio_channel_attr_read_all() +- iio_channel_attr_read_bool() +- iio_channel_attr_read_longlong() +- iio_channel_attr_read_double() + +Read debug attributes with those functions: +- iio_device_debug_attr_read() +- iio_device_debug_attr_read_all() +- iio_device_debug_attr_read_bool() +- iio_device_debug_attr_read_longlong() +- iio_device_debug_attr_read_double() + +### Modifying a parameter +Write device-specific attributes with those functions: +- iio_device_attr_write() +- iio_device_attr_write_all() +- iio_device_attr_write_bool() +- iio_device_attr_write_longlong() +- iio_device_attr_write_double() + +Write channel-specific attributes with those functions: +- iio_channel_attr_write() +- iio_channel_attr_write_all() +- iio_channel_attr_write_bool() +- iio_channel_attr_write_longlong() +- iio_channel_attr_write_double() + +Write debug attributes with those functions: +- iio_device_debug_attr_write() +- iio_device_debug_attr_write_all() +- iio_device_debug_attr_write_bool() +- iio_device_debug_attr_write_longlong() +- iio_device_debug_attr_write_double() + +## Triggers +Some devices, mostly low-speed ADCs and DACs, require a trigger to be set for the capture or upload process to work. + +In libiio, triggers are just regular iio_device objects. To check if an iio_device can be used as a trigger, you can use iio_device_is_trigger(). + +To see if one device is associated with a trigger, use iio_device_get_trigger(). + +To assign one trigger to a iio_device, you can use iio_device_set_trigger(). If you want to disassociate a iio_device from its trigger, pass NULL to the "trigger" parameter of this function. + +## Capturing or uploading samples +The process of capturing samples from the hardware and uploading samples to the hardware is done using the functions that apply to the iio_buffer object. + +### Enabling channels and creating the Buffer object +The very first step is to enable the capture channels that we want to use, and disable those that we don't need. +This is done with the functions iio_channel_enable() and iio_channel_disable(). +Note that the channels will really be enabled or disabled when the iio_buffer object is created. + +Also, not all channels can be enabled. To know whether or not one channel can be enabled, use iio_channel_is_scan_element(). + +Once the channels have been enabled, and triggers assigned (for triggered buffers) the iio_buffer object can be created from the iio_device object that will be used, with the function iio_device_create_buffer(). +This call will fail if no channels have been enabled, or for triggered buffers, if the trigger has not been assigned. + +When the object is no more needed, it can be destroyed with iio_buffer_destroy(). + +### Refilling the Buffer (input devices only) +If the Buffer object has been created from a device with input channels, then it must be updated first. This is done with the iio_buffer_refill() function. + +### Reading or writing samples to the Buffer +libiio offers various ways to interact with the iio_buffer object. + +#### Direct copy +If you already have a buffer of samples, correctly interleaved and in the format that the hardware expects, +it is possible to copy the samples directly into the iio_buffer object using `memcpy`: + +```c +size_t iio_buf_size = iio_buffer_end(buffer) - iio_buffer_start(buffer); +size_t count = MAX(sizeof(samples_buffer), iio_buf_size); +memcpy(iio_buffer_start(buffer), samples_buffer, count); +``` + +Using `memcpy` to copy samples from the iio_buffer is not recommended. +When capturing samples from an input device, you cannot assume that the iio_buffer object contains only the samples you're interested in. + +#### Iterating over the buffer with a callback +libiio provides a way to iterate over the buffer by registering a callback function, with the iio_buffer_foreach_sample() function. + +The callback function will be called for each "sample slot" of the buffer, +which will contain a valid sample if the buffer has been refilled, +or correspond to an area where a sample should be stored if using an output device. + +```c +ssize_t sample_cb(const struct iio_channel *chn, void *src, size_t bytes, void *d) +{ + /* Use "src" to read or write a sample for this channel */ +} + +int main(void) +{ + ... + iio_buffer_foreach_sample(buffer, sample_cb, NULL); + ... +} +``` + +Note that the callback will be called in the order that the samples appear in the buffer, +and only for samples that correspond to channels that were enabled. + +#### Iterating on the samples with a for loop +This method allows you to iterate over the samples slots that correspond to one channel. +As such, it is interesting if you want to process the data channel by channel. + +It basically consists in a for loop that uses the functions iio_buffer_first(), iio_buffer_step() and iio_buffer_end(): + +```c +for (void *ptr = iio_buffer_first(buffer, channel); + ptr < iio_buffer_end(buffer); + ptr += iio_buffer_step(buffer)) { + /* Use "ptr" to read or write a sample for this channel */ +} +``` + +#### Extracting from/to a second buffer + +Finally, it is possible to use the iio_channel_read() and iio_channel_read_raw() +functions to read samples from the iio_buffer to a second byte array. +The samples will be deinterleaved if needed. +The "raw" variant will only deinterleave the samples, while the other variant will deinterleave and convert the samples. + +For output devices, the iio_channel_write() and iio_channel_write_raw() functions are also available. +The "raw" variant will only interleave the samples (if needed), while the other variant will interleave and convert the samples +back to their hardware format. + +#### Convert the samples from/to hardware format +The raw stream of samples generally isn't in a format that can be directly used in algorithms. +Some operations, like endianness conversion and bit-shifting of the samples, have to be performed first. + +libiio offers two functions that can be used to convert samples: +- iio_channel_convert(), to convert from the hardware format +- iio_channel_convert_inverse(), to convert to the hardware format. + +Those two functions should always be used when manipulating the samples of the iio_buffer. +The exception is when iio_channel_read() or iio_channel_write() are used, as the conversion is then done internally. + +#### Submitting the Buffer (output devices only) +When all the samples have been written to the iio_buffer object, you can submit the buffer to the hardware with a call to iio_buffer_push(). +As soon as the buffer has been submitted, it can be re-used to store new samples. + +If the iio_buffer object has been created with the "cyclic" parameter set, and the kernel driver supports cyclic buffers, +the submitted buffer will be repeated until the iio_buffer is destroyed, and no subsequent call to iio_buffer_push() will be allowed. + +## Advanced options + +### Register and retrieve a pointer +The iio_device and iio_channel allow you to register a pointer, that can then be retrieved at a later moment. +- A pointer can be registered with a iio_device object using iio_device_set_data(), and can be retrieved with iio_device_get_data(). +- A pointer can be registered with a iio_channel object using iio_channel_set_data(), and can be retrieved with iio_channel_get_data(). + +### Debug attributes +Some IIO devices provide debug parameters, but their presence is optional. In a similar way than with regular device parameters, +the number of debug parameters can be obtained with iio_device_get_debug_attrs_count(). Each individual parameter can be retrieved with iio_device_get_debug_attr(). +Alternatively, it is possible to lookup for the name of a debug attribute with iio_device_find_debug_attr(). + +Those debug parameters can be read using the following functions: +- iio_device_debug_attr_read(), +- iio_device_debug_attr_read_all(), +- iio_device_debug_attr_read_bool(), +- iio_device_debug_attr_read_longlong(), +- iio_device_debug_attr_read_double(). + +Those debug parameters can be written using the following functions: +- iio_device_debug_attr_write(), +- iio_device_debug_attr_write_all(), +- iio_device_debug_attr_write_bool(), +- iio_device_debug_attr_write_longlong(), +- iio_device_debug_attr_write_double(). + +### Reading and writing registers +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(). + +## 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. + +\example iio_attr.c Part of the libiio-utilites, iio_attr is a utility for displaying information about local or remote IIO devices. By providing an optional value, iio_attr will attempt to write the new value to the attribute. +\example iio_info.c Part of the libiio-utilites, iio_info is a utility for displaying information about local or remote IIO devices. +\example iio_readdev.c Part of the libiio-utilites, iio_readdev is a utility for reading buffers from connected IIO devices, and sending results to standard out. +\example iio_writedev.c Part of the libiio-utilites, iio_writedev is a utility for writing buffers from connected IIO devices. +\example iio_reg.c Part of the libiio-utilites, iio_reg is a utility for debugging local IIO devices. It should not be used by normal users, and is normally used by driver developers during development, or by end users debugging a driver, or sending in a feature request. It provides a mechanism to read or write SPI or I2C registers for IIO devices. +\example ad9361-iiostream.c This example libiio program is meant to exercise the features of IIO functionality on the AD9361 found on the AD-FMCOMMS2-EBZ, AD-FMCOMMS3-EBZ, and the ADRV9361-Z7035 RF SOM. +\example ad9371-iiostream.c This example libiio program is meant to exercise the features of IIO functionality on the AD9371. +\example adrv9009-iiostream.c This example libiio program is meant to exercise the features of IIO functionality on the ADRV9009. +\example 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. +\example iio-monitor.c A Curses based application which implements real time monitoring of IIO non-buffer samples. +@if CPP_BINDINGS +\example iiopp-enum.cpp This example demonstrates usage of the C++ API to enumerate devices, channels and attributes. +@endif +*/