On 04/08/15 15:20, Daniel Baluta wrote: > This is intended to help developers faster find their way > inside the Industrial I/O core and reduce time spent on IIO > drivers development. > > Signed-off-by: Daniel Baluta <daniel.baluta@xxxxxxxxx> > Acked-by: Crt Mori <cmo@xxxxxxxxxxx> > Reviewed-by: Lars-Peter Clausen <lars@xxxxxxxxxx> Applied to the togreg branch of iio.git. Lets fine tune this / expand it in place. Thanks for doing this Daniel, it is great to have this. Jonathan > --- > Documentation/DocBook/Makefile | 2 +- > Documentation/DocBook/iio.tmpl | 697 +++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 698 insertions(+), 1 deletion(-) > create mode 100644 Documentation/DocBook/iio.tmpl > > diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile > index b6a6a2e..9e08606 100644 > --- a/Documentation/DocBook/Makefile > +++ b/Documentation/DocBook/Makefile > @@ -15,7 +15,7 @@ DOCBOOKS := z8530book.xml device-drivers.xml \ > 80211.xml debugobjects.xml sh.xml regulator.xml \ > alsa-driver-api.xml writing-an-alsa-driver.xml \ > tracepoint.xml drm.xml media_api.xml w1.xml \ > - writing_musb_glue_layer.xml crypto-API.xml > + writing_musb_glue_layer.xml crypto-API.xml iio.xml > > include Documentation/DocBook/media/Makefile > > diff --git a/Documentation/DocBook/iio.tmpl b/Documentation/DocBook/iio.tmpl > new file mode 100644 > index 0000000..06bb53d > --- /dev/null > +++ b/Documentation/DocBook/iio.tmpl > @@ -0,0 +1,697 @@ > +<?xml version="1.0" encoding="UTF-8"?> > +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" > + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []> > + > +<book id="iioid"> > + <bookinfo> > + <title>Industrial I/O driver developer's guide </title> > + > + <authorgroup> > + <author> > + <firstname>Daniel</firstname> > + <surname>Baluta</surname> > + <affiliation> > + <address> > + <email>daniel.baluta@xxxxxxxxx</email> > + </address> > + </affiliation> > + </author> > + </authorgroup> > + > + <copyright> > + <year>2015</year> > + <holder>Intel Corporation</holder> > + </copyright> > + > + <legalnotice> > + <para> > + This documentation is free software; you can redistribute > + it and/or modify it under the terms of the GNU General Public > + License version 2. > + </para> > + </legalnotice> > + </bookinfo> > + > + <toc></toc> > + > + <chapter id="intro"> > + <title>Introduction</title> > + <para> > + The main purpose of the Industrial I/O subsystem (IIO) is to provide > + support for devices that in some sense perform either analog-to-digital > + conversion (ADC) or digital-to-analog conversion (DAC) or both. The aim > + is to fill the gap between the somewhat similar hwmon and input > + subsystems. > + Hwmon is directed at low sample rate sensors used to monitor and > + control the system itself, like fan speed control or temperature > + measurement. Input is, as its name suggests, focused on human interaction > + input devices (keyboard, mouse, touchscreen). In some cases there is > + considerable overlap between these and IIO. > + </para> > + <para> > + Devices that fall into this category include: > + <itemizedlist> > + <listitem> > + analog to digital converters (ADCs) > + </listitem> > + <listitem> > + accelerometers > + </listitem> > + <listitem> > + capacitance to digital converters (CDCs) > + </listitem> > + <listitem> > + digital to analog converters (DACs) > + </listitem> > + <listitem> > + gyroscopes > + </listitem> > + <listitem> > + inertial measurement units (IMUs) > + </listitem> > + <listitem> > + color and light sensors > + </listitem> > + <listitem> > + magnetometers > + </listitem> > + <listitem> > + pressure sensors > + </listitem> > + <listitem> > + proximity sensors > + </listitem> > + <listitem> > + temperature sensors > + </listitem> > + </itemizedlist> > + Usually these sensors are connected via SPI or I2C. A common use case of the > + sensors devices is to have combined functionality (e.g. light plus proximity > + sensor). > + </para> > + </chapter> > + <chapter id='iiosubsys'> > + <title>Industrial I/O core</title> > + <para> > + The Industrial I/O core offers: > + <itemizedlist> > + <listitem> > + a unified framework for writing drivers for many different types of > + embedded sensors. > + </listitem> > + <listitem> > + a standard interface to user space applications manipulating sensors. > + </listitem> > + </itemizedlist> > + The implementation can be found under <filename> > + drivers/iio/industrialio-*</filename> > + </para> > + <sect1 id="iiodevice"> > + <title> Industrial I/O devices </title> > + > +!Finclude/linux/iio/iio.h iio_dev > +!Fdrivers/iio/industrialio-core.c iio_device_alloc > +!Fdrivers/iio/industrialio-core.c iio_device_free > +!Fdrivers/iio/industrialio-core.c iio_device_register > +!Fdrivers/iio/industrialio-core.c iio_device_unregister > + > + <para> > + An IIO device usually corresponds to a single hardware sensor and it > + provides all the information needed by a driver handling a device. > + Let's first have a look at the functionality embedded in an IIO > + device then we will show how a device driver makes use of an IIO > + device. > + </para> > + <para> > + There are two ways for a user space application to interact > + with an IIO driver. > + <itemizedlist> > + <listitem> > + <filename>/sys/bus/iio/iio:deviceX/</filename>, this > + represents a hardware sensor and groups together the data > + channels of the same chip. > + </listitem> > + <listitem> > + <filename>/dev/iio:deviceX</filename>, character device node > + interface used for buffered data transfer and for events information > + retrieval. > + </listitem> > + </itemizedlist> > + </para> > + A typical IIO driver will register itself as an I2C or SPI driver and will > + create two routines, <function> probe </function> and <function> remove > + </function>. At <function>probe</function>: > + <itemizedlist> > + <listitem>call <function>iio_device_alloc</function>, which allocates memory > + for an IIO device. > + </listitem> > + <listitem> initialize IIO device fields with driver specific information > + (e.g. device name, device channels). > + </listitem> > + <listitem>call <function> iio_device_register</function>, this registers the > + device with the IIO core. After this call the device is ready to accept > + requests from user space applications. > + </listitem> > + </itemizedlist> > + At <function>remove</function>, we free the resources allocated in > + <function>probe</function> in reverse order: > + <itemizedlist> > + <listitem><function>iio_device_unregister</function>, unregister the device > + from the IIO core. > + </listitem> > + <listitem><function>iio_device_free</function>, free the memory allocated > + for the IIO device. > + </listitem> > + </itemizedlist> > + > + <sect2 id="iioattr"> <title> IIO device sysfs interface </title> > + <para> > + Attributes are sysfs files used to expose chip info and also allowing > + applications to set various configuration parameters. For device > + with index X, attributes can be found under > + <filename>/sys/bus/iio/iio:deviceX/ </filename> directory. > + Common attributes are: > + <itemizedlist> > + <listitem><filename>name</filename>, description of the physical > + chip. > + </listitem> > + <listitem><filename>dev</filename>, shows the major:minor pair > + associated with <filename>/dev/iio:deviceX</filename> node. > + </listitem> > + <listitem><filename>sampling_frequency_available</filename>, > + available discrete set of sampling frequency values for > + device. > + </listitem> > + </itemizedlist> > + Available standard attributes for IIO devices are described in the > + <filename>Documentation/ABI/testing/sysfs-bus-iio </filename> file > + in the Linux kernel sources. > + </para> > + </sect2> > + <sect2 id="iiochannel"> <title> IIO device channels </title> > +!Finclude/linux/iio/iio.h iio_chan_spec structure. > + <para> > + An IIO device channel is a representation of a data channel. An > + IIO device can have one or multiple channels. For example: > + <itemizedlist> > + <listitem> > + a thermometer sensor has one channel representing the > + temperature measurement. > + </listitem> > + <listitem> > + a light sensor with two channels indicating the measurements in > + the visible and infrared spectrum. > + </listitem> > + <listitem> > + an accelerometer can have up to 3 channels representing > + acceleration on X, Y and Z axes. > + </listitem> > + </itemizedlist> > + An IIO channel is described by the <type> struct iio_chan_spec > + </type>. A thermometer driver for the temperature sensor in the > + example above would have to describe its channel as follows: > + <programlisting> > + static const struct iio_chan_spec temp_channel[] = { > + { > + .type = IIO_TEMP, > + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), > + }, > + }; > + > + </programlisting> > + Channel sysfs attributes exposed to userspace are specified in > + the form of <emphasis>bitmasks</emphasis>. Depending on their > + shared info, attributes can be set in one of the following masks: > + <itemizedlist> > + <listitem><emphasis>info_mask_separate</emphasis>, attributes will > + be specific to this channel</listitem> > + <listitem><emphasis>info_mask_shared_by_type</emphasis>, > + attributes are shared by all channels of the same type</listitem> > + <listitem><emphasis>info_mask_shared_by_dir</emphasis>, attributes > + are shared by all channels of the same direction </listitem> > + <listitem><emphasis>info_mask_shared_by_all</emphasis>, > + attributes are shared by all channels</listitem> > + </itemizedlist> > + When there are multiple data channels per channel type we have two > + ways to distinguish between them: > + <itemizedlist> > + <listitem> set <emphasis> .modified</emphasis> field of <type> > + iio_chan_spec</type> to 1. Modifiers are specified using > + <emphasis>.channel2</emphasis> field of the same > + <type>iio_chan_spec</type> structure and are used to indicate a > + physically unique characteristic of the channel such as its direction > + or spectral response. For example, a light sensor can have two channels, > + one for infrared light and one for both infrared and visible light. > + </listitem> > + <listitem> set <emphasis>.indexed </emphasis> field of > + <type>iio_chan_spec</type> to 1. In this case the channel is > + simply another instance with an index specified by the > + <emphasis>.channel</emphasis> field. > + </listitem> > + </itemizedlist> > + Here is how we can make use of the channel's modifiers: > + <programlisting> > + static const struct iio_chan_spec light_channels[] = { > + { > + .type = IIO_INTENSITY, > + .modified = 1, > + .channel2 = IIO_MOD_LIGHT_IR, > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > + .info_mask_shared = BIT(IIO_CHAN_INFO_SAMP_FREQ), > + }, > + { > + .type = IIO_INTENSITY, > + .modified = 1, > + .channel2 = IIO_MOD_LIGHT_BOTH, > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > + .info_mask_shared = BIT(IIO_CHAN_INFO_SAMP_FREQ), > + }, > + { > + .type = IIO_LIGHT, > + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), > + .info_mask_shared = BIT(IIO_CHAN_INFO_SAMP_FREQ), > + }, > + > + } > + </programlisting> > + This channel's definition will generate two separate sysfs files > + for raw data retrieval: > + <itemizedlist> > + <listitem> > + <filename>/sys/bus/iio/iio:deviceX/in_intensity_ir_raw</filename> > + </listitem> > + <listitem> > + <filename>/sys/bus/iio/iio:deviceX/in_intensity_both_raw</filename> > + </listitem> > + </itemizedlist> > + one file for processed data: > + <itemizedlist> > + <listitem> > + <filename>/sys/bus/iio/iio:deviceX/in_illuminance_input > + </filename> > + </listitem> > + </itemizedlist> > + and one shared sysfs file for sampling frequency: > + <itemizedlist> > + <listitem> > + <filename>/sys/bus/iio/iio:deviceX/sampling_frequency. > + </filename> > + </listitem> > + </itemizedlist> > + </para> > + <para> > + Here is how we can make use of the channel's indexing: > + <programlisting> > + static const struct iio_chan_spec light_channels[] = { > + { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > + }, > + { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 1, > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > + }, > + } > + </programlisting> > + This will generate two separate attributes files for raw data > + retrieval: > + <itemizedlist> > + <listitem> > + <filename>/sys/bus/iio/devices/iio:deviceX/in_voltage0_raw</filename>, > + representing voltage measurement for channel 0. > + </listitem> > + <listitem> > + <filename>/sys/bus/iio/devices/iio:deviceX/in_voltage1_raw</filename>, > + representing voltage measurement for channel 1. > + </listitem> > + </itemizedlist> > + </para> > + </sect2> > + </sect1> > + > + <sect1 id="iiobuffer"> <title> Industrial I/O buffers </title> > +!Finclude/linux/iio/buffer.h iio_buffer > +!Edrivers/iio/industrialio-buffer.c > + > + <para> > + The Industrial I/O core offers a way for continuous data capture > + based on a trigger source. Multiple data channels can be read at once > + from <filename>/dev/iio:deviceX</filename> character device node, > + thus reducing the CPU load. > + </para> > + > + <sect2 id="iiobuffersysfs"> > + <title>IIO buffer sysfs interface </title> > + <para> > + An IIO buffer has an associated attributes directory under <filename> > + /sys/bus/iio/iio:deviceX/buffer/</filename>. Here are the existing > + attributes: > + <itemizedlist> > + <listitem> > + <emphasis>length</emphasis>, the total number of data samples > + (capacity) that can be stored by the buffer. > + </listitem> > + <listitem> > + <emphasis>enable</emphasis>, activate buffer capture. > + </listitem> > + </itemizedlist> > + > + </para> > + </sect2> > + <sect2 id="iiobuffersetup"> <title> IIO buffer setup </title> > + <para>The meta information associated with a channel reading > + placed in a buffer is called a <emphasis> scan element </emphasis>. > + The important bits configuring scan elements are exposed to > + userspace applications via the <filename> > + /sys/bus/iio/iio:deviceX/scan_elements/</filename> directory. This > + file contains attributes of the following form: > + <itemizedlist> > + <listitem><emphasis>enable</emphasis>, used for enabling a channel. > + If and only if its attribute is non zero, then a triggered capture > + will contain data samples for this channel. > + </listitem> > + <listitem><emphasis>type</emphasis>, description of the scan element > + data storage within the buffer and hence the form in which it is > + read from user space. Format is <emphasis> > + [be|le]:[s|u]bits/storagebitsXrepeat[>>shift] </emphasis>. > + <itemizedlist> > + <listitem> <emphasis>be</emphasis> or <emphasis>le</emphasis>, specifies > + big or little endian. > + </listitem> > + <listitem> > + <emphasis>s </emphasis>or <emphasis>u</emphasis>, specifies if > + signed (2's complement) or unsigned. > + </listitem> > + <listitem><emphasis>bits</emphasis>, is the number of valid data > + bits. > + </listitem> > + <listitem><emphasis>storagebits</emphasis>, is the number of bits > + (after padding) that it occupies in the buffer. > + </listitem> > + <listitem> > + <emphasis>shift</emphasis>, if specified, is the shift that needs > + to be applied prior to masking out unused bits. > + </listitem> > + <listitem> > + <emphasis>repeat</emphasis>, specifies the number of bits/storagebits > + repetitions. When the repeat element is 0 or 1, then the repeat > + value is omitted. > + </listitem> > + </itemizedlist> > + </listitem> > + </itemizedlist> > + For example, a driver for a 3-axis accelerometer with 12 bit > + resolution where data is stored in two 8-bits registers as > + follows: > + <programlisting> > + 7 6 5 4 3 2 1 0 > + +---+---+---+---+---+---+---+---+ > + |D3 |D2 |D1 |D0 | X | X | X | X | (LOW byte, address 0x06) > + +---+---+---+---+---+---+---+---+ > + > + 7 6 5 4 3 2 1 0 > + +---+---+---+---+---+---+---+---+ > + |D11|D10|D9 |D8 |D7 |D6 |D5 |D4 | (HIGH byte, address 0x07) > + +---+---+---+---+---+---+---+---+ > + </programlisting> > + > + will have the following scan element type for each axis: > + <programlisting> > + $ cat /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_y_type > + le:s12/16>>4 > + </programlisting> > + A user space application will interpret data samples read from the > + buffer as two byte little endian signed data, that needs a 4 bits > + right shift before masking out the 12 valid bits of data. > + </para> > + <para> > + For implementing buffer support a driver should initialize the following > + fields in <type>iio_chan_spec</type> definition: > + <programlisting> > + struct iio_chan_spec { > + /* other members */ > + int scan_index > + struct { > + char sign; > + u8 realbits; > + u8 storagebits; > + u8 shift; > + u8 repeat; > + enum iio_endian endianness; > + } scan_type; > + }; > + </programlisting> > + The driver implementing the accelerometer described above will > + have the following channel definition: > + <programlisting> > + struct struct iio_chan_spec accel_channels[] = { > + { > + .type = IIO_ACCEL, > + .modified = 1, > + .channel2 = IIO_MOD_X, > + /* other stuff here */ > + .scan_index = 0, > + .scan_type = { > + .sign = 's', > + .realbits = 12, > + .storgebits = 16, > + .shift = 4, > + .endianness = IIO_LE, > + }, > + } > + /* similar for Y (with channel2 = IIO_MOD_Y, scan_index = 1) > + * and Z (with channel2 = IIO_MOD_Z, scan_index = 2) axis > + */ > + } > + </programlisting> > + </para> > + <para> > + Here <emphasis> scan_index </emphasis> defines the order in which > + the enabled channels are placed inside the buffer. Channels with a lower > + scan_index will be placed before channels with a higher index. Each > + channel needs to have a unique scan_index. > + </para> > + <para> > + Setting scan_index to -1 can be used to indicate that the specific > + channel does not support buffered capture. In this case no entries will > + be created for the channel in the scan_elements directory. > + </para> > + </sect2> > + </sect1> > + > + <sect1 id="iiotrigger"> <title> Industrial I/O triggers </title> > +!Finclude/linux/iio/trigger.h iio_trigger > +!Edrivers/iio/industrialio-trigger.c > + <para> > + In many situations it is useful for a driver to be able to > + capture data based on some external event (trigger) as opposed > + to periodically polling for data. An IIO trigger can be provided > + by a device driver that also has an IIO device based on hardware > + generated events (e.g. data ready or threshold exceeded) or > + provided by a separate driver from an independent interrupt > + source (e.g. GPIO line connected to some external system, timer > + interrupt or user space writing a specific file in sysfs). A > + trigger may initiate data capture for a number of sensors and > + also it may be completely unrelated to the sensor itself. > + </para> > + > + <sect2 id="iiotrigsysfs"> <title> IIO trigger sysfs interface </title> > + There are two locations in sysfs related to triggers: > + <itemizedlist> > + <listitem><filename>/sys/bus/iio/devices/triggerY</filename>, > + this file is created once an IIO trigger is registered with > + the IIO core and corresponds to trigger with index Y. Because > + triggers can be very different depending on type there are few > + standard attributes that we can describe here: > + <itemizedlist> > + <listitem> > + <emphasis>name</emphasis>, trigger name that can be later > + used for association with a device. > + </listitem> > + <listitem> > + <emphasis>sampling_frequency</emphasis>, some timer based > + triggers use this attribute to specify the frequency for > + trigger calls. > + </listitem> > + </itemizedlist> > + </listitem> > + <listitem> > + <filename>/sys/bus/iio/devices/iio:deviceX/trigger/</filename>, this > + directory is created once the device supports a triggered > + buffer. We can associate a trigger with our device by writing > + the trigger's name in the <filename>current_trigger</filename> file. > + </listitem> > + </itemizedlist> > + </sect2> > + > + <sect2 id="iiotrigattr"> <title> IIO trigger setup</title> > + > + <para> > + Let's see a simple example of how to setup a trigger to be used > + by a driver. > + > + <programlisting> > + struct iio_trigger_ops trigger_ops = { > + .set_trigger_state = sample_trigger_state, > + .validate_device = sample_validate_device, > + } > + > + struct iio_trigger *trig; > + > + /* first, allocate memory for our trigger */ > + trig = iio_trigger_alloc(dev, "trig-%s-%d", name, idx); > + > + /* setup trigger operations field */ > + trig->ops = &trigger_ops; > + > + /* now register the trigger with the IIO core */ > + iio_trigger_register(trig); > + </programlisting> > + </para> > + </sect2> > + > + <sect2 id="iiotrigsetup"> <title> IIO trigger ops</title> > +!Finclude/linux/iio/trigger.h iio_trigger_ops > + <para> > + Notice that a trigger has a set of operations attached: > + <itemizedlist> > + <listitem> > + <function>set_trigger_state</function>, switch the trigger on/off > + on demand. > + </listitem> > + <listitem> > + <function>validate_device</function>, function to validate the > + device when the current trigger gets changed. > + </listitem> > + </itemizedlist> > + </para> > + </sect2> > + </sect1> > + <sect1 id="iiotriggered_buffer"> > + <title> Industrial I/O triggered buffers </title> > + <para> > + Now that we know what buffers and triggers are let's see how they > + work together. > + </para> > + <sect2 id="iiotrigbufsetup"> <title> IIO triggered buffer setup</title> > +!Edrivers/iio/industrialio-triggered-buffer.c > +!Finclude/linux/iio/iio.h iio_buffer_setup_ops > + > + > + <para> > + A typical triggered buffer setup looks like this: > + <programlisting> > + const struct iio_buffer_setup_ops sensor_buffer_setup_ops = { > + .preenable = sensor_buffer_preenable, > + .postenable = sensor_buffer_postenable, > + .postdisable = sensor_buffer_postdisable, > + .predisable = sensor_buffer_predisable, > + }; > + > + irqreturn_t sensor_iio_pollfunc(int irq, void *p) > + { > + pf->timestamp = iio_get_time_ns(); > + return IRQ_WAKE_THREAD; > + } > + > + irqreturn_t sensor_trigger_handler(int irq, void *p) > + { > + u16 buf[8]; > + int i = 0; > + > + /* read data for each active channel */ > + for_each_set_bit(bit, active_scan_mask, masklength) > + buf[i++] = sensor_get_data(bit) > + > + iio_push_to_buffers_with_timestamp(indio_dev, buf, timestamp); > + > + iio_trigger_notify_done(trigger); > + return IRQ_HANDLED; > + } > + > + /* setup triggered buffer, usually in probe function */ > + iio_triggered_buffer_setup(indio_dev, sensor_iio_polfunc, > + sensor_trigger_handler, > + sensor_buffer_setup_ops); > + </programlisting> > + </para> > + The important things to notice here are: > + <itemizedlist> > + <listitem><function> iio_buffer_setup_ops</function>, the buffer setup > + functions to be called at predefined points in the buffer configuration > + sequence (e.g. before enable, after disable). If not specified, the > + IIO core uses the default <type>iio_triggered_buffer_setup_ops</type>. > + </listitem> > + <listitem><function>sensor_iio_pollfunc</function>, the function that > + will be used as top half of poll function. It should do as little > + processing as possible, because it runs in interrupt context. The most > + common operation is recording of the current timestamp and for this reason > + one can use the IIO core defined <function>iio_pollfunc_store_time > + </function> function. > + </listitem> > + <listitem><function>sensor_trigger_handler</function>, the function that > + will be used as bottom half of the poll function. This runs in the > + context of a kernel thread and all the processing takes place here. > + It usually reads data from the device and stores it in the internal > + buffer together with the timestamp recorded in the top half. > + </listitem> > + </itemizedlist> > + </sect2> > + </sect1> > + </chapter> > + <chapter id='iioresources'> > + <title> Resources </title> > + IIO core may change during time so the best documentation to read is the > + source code. There are several locations where you should look: > + <itemizedlist> > + <listitem> > + <filename>drivers/iio/</filename>, contains the IIO core plus > + and directories for each sensor type (e.g. accel, magnetometer, > + etc.) > + </listitem> > + <listitem> > + <filename>include/linux/iio/</filename>, contains the header > + files, nice to read for the internal kernel interfaces. > + </listitem> > + <listitem> > + <filename>include/uapi/linux/iio/</filename>, contains files to be > + used by user space applications. > + </listitem> > + <listitem> > + <filename>tools/iio/</filename>, contains tools for rapidly > + testing buffers, events and device creation. > + </listitem> > + <listitem> > + <filename>drivers/staging/iio/</filename>, contains code for some > + drivers or experimental features that are not yet mature enough > + to be moved out. > + </listitem> > + </itemizedlist> > + <para> > + Besides the code, there are some good online documentation sources: > + <itemizedlist> > + <listitem> > + <ulink url="http://marc.info/?l=linux-iio"> Industrial I/O mailing > + list </ulink> > + </listitem> > + <listitem> > + <ulink url="http://wiki.analog.com/software/linux/docs/iio/iio"> > + Analog Device IIO wiki page </ulink> > + </listitem> > + <listitem> > + <ulink url="https://fosdem.org/2015/schedule/event/iiosdr/"> > + Using the Linux IIO framework for SDR, Lars-Peter Clausen's > + presentation at FOSDEM </ulink> > + </listitem> > + </itemizedlist> > + </para> > + </chapter> > +</book> > + > +<!-- > +vim: softtabstop=2:shiftwidth=2:expandtab:textwidth=72 > +--> > -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html