IIO Triggered Buffer Support Guide
Free Linux Kernel Development Course — Chapter 10, Lecture 6
| ← PREV_LEC | NEXT_LEC → |
Free Embedded Systems Course
Free Linux Device Drivers Course
Free Embedded Linux Course
IIO triggered buffer support is the mechanism the Linux Industrial I/O framework uses to capture sensor samples the moment a trigger fires, instead of forcing user space to poll a device one channel at a time. If you have followed this free Linux kernel development course from the start of Chapter 10, you already have a working iio_chan_spec array with indexed and modified channels. In this lecture we attach a real trigger to that device, write the top-half and bottom-half handlers, and stream continuous samples out through a character device buffer — all using the current kernel 6.x resource-managed APIs.
What You Will Learn
- Why triggered buffer capture exists and how it differs from single-shot sysfs reads
- The three common in-tree IIO triggers: interrupt, hrtimer, and sysfs
- The top-half / bottom-half split used by every triggered IIO driver
- The
iio_buffer_setup_opslifecycle callbacks - How to write an original driver with
devm_iio_triggered_buffer_setup() - How to test the driver from user space and read expected buffer output
Prerequisites
- Completion of lddch10_1 through lddch10_5 (IIO channel model, iio_chan_spec, indexed and modified channels)
- Comfort writing a basic platform driver probe/remove function
- A kernel build tree of version 6.x with
CONFIG_IIO,CONFIG_IIO_BUFFER, andCONFIG_IIO_TRIGGERED_BUFFERenabled
Why IIO Triggered Buffer Support Matters
Most sensors do not need to be read continuously. A temperature sensor read once a second is fine with a plain sysfs in_temp_raw file. But an accelerometer feeding a vibration analysis pipeline, or an ADC sampling an audio-rate signal, needs many channels captured together at a fixed rate. That is exactly what triggered buffer support solves: a trigger event fires, the driver snapshots every enabled channel in one shot, and user space reads the whole set as a single timestamped record from a character device.
Trigger Source
IRQ / hrtimer / sysfs
→
Top Half
pollfunc (IRQ context)
→
Bottom Half
trigger_handler (kthread)
→
IIO Buffer
/dev/iio:deviceX
Common IIO Trigger Types
A trigger is a separate kernel object from the IIO device itself. One trigger can drive many devices, and a device only needs a trigger if it supports buffered capture. The IIO core ships several ready-made trigger drivers so you do not have to write your own while learning:
| Trigger Driver | Kernel Config | Fires From |
|---|---|---|
| iio-trig-interrupt | CONFIG_IIO_INTERRUPT_TRIGGER | Any hardware IRQ line |
| iio-trig-hrtimer | CONFIG_IIO_HRTIMER_TRIGGER | High-resolution kernel timer, configured via configfs |
| iio-trig-sysfs | CONFIG_IIO_SYSFS_TRIGGER | A write to a sysfs file from user space |
For development on a board without a real trigger source, iio-trig-hrtimer is the easiest choice since it needs no external hardware — you create it through configfs, as shown later in the testing section.
The iio_buffer_setup_ops Lifecycle
Before and after the buffer is enabled or disabled, the IIO core calls four optional callbacks. Most drivers only need postenable and predisable to start or stop hardware sampling.
→
postenable (start sampling)
→
… buffer active …
→
predisable (stop sampling)
→
postdisable
Writing an Original Triggered Buffer Driver
The example below, ep_iio_trigbuf_demo, is an original teaching driver built for this course. It exposes two indexed voltage channels plus a mandatory timestamp channel, and streams all three together whenever the assigned trigger fires.
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
struct ep_trigbuf_data {
struct mutex lock;
u16 chan_val[2];
};
static int ep_trigbuf_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct ep_trigbuf_data *st = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&st->lock);
*val = st->chan_val[chan->channel];
mutex_unlock(&st->lock);
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static const struct iio_info ep_trigbuf_info = {
.read_raw = ep_trigbuf_read_raw,
};
#define EP_TRIGBUF_CHANNEL(idx) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = idx, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.scan_index = idx, \
.scan_type = { \
.sign = 'u', \
.realbits = 12, \
.storagebits = 16, \
.endianness = IIO_CPU, \
}, \
}
static const struct iio_chan_spec ep_trigbuf_channels[] = {
EP_TRIGBUF_CHANNEL(0),
EP_TRIGBUF_CHANNEL(1),
IIO_CHAN_SOFT_TIMESTAMP(2),
};
static irqreturn_t ep_trigbuf_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ep_trigbuf_data *st = iio_priv(indio_dev);
u16 buf[8] = { 0 }; /* room for channel data + padding for timestamp */
int i = 0, bit;
mutex_lock(&st->lock);
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength)
buf[i++] = st->chan_val[bit];
mutex_unlock(&st->lock);
iio_push_to_buffers_with_timestamp(indio_dev, buf,
iio_get_time_ns(indio_dev));
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int ep_trigbuf_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct ep_trigbuf_data *st;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
mutex_init(&st->lock);
st->chan_val[0] = 512;
st->chan_val[1] = 900;
indio_dev->name = "ep_iio_trigbuf_demo";
indio_dev->info = &ep_trigbuf_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ep_trigbuf_channels;
indio_dev->num_channels = ARRAY_SIZE(ep_trigbuf_channels);
ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev,
iio_pollfunc_store_time,
ep_trigbuf_trigger_handler,
NULL);
if (ret)
return ret;
return devm_iio_device_register(&pdev->dev, indio_dev);
}
static struct platform_driver ep_trigbuf_driver = {
.driver = {
.name = "ep_iio_trigbuf_demo",
},
.probe = ep_trigbuf_probe,
};
module_platform_driver(ep_trigbuf_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("EP IIO triggered buffer demo driver");
Notice that ep_trigbuf_probe() has no matching remove function at all. Both devm_iio_triggered_buffer_setup() and devm_iio_device_register() are resource-managed: the kernel tears down the buffer and unregisters the device automatically when the platform device detaches, in the correct order.
Modern vs Legacy Triggered Buffer APIs
| Function | Cleanup | When to Use |
|---|---|---|
| iio_triggered_buffer_setup() | Manual iio_triggered_buffer_cleanup() in remove() | Legacy drivers only, or when you need non-devm control over teardown order |
| devm_iio_triggered_buffer_setup() | Automatic on device detach | Recommended default for new kernel 6.x drivers |
| devm_iio_triggered_buffer_setup_ext() | Automatic on device detach | Same as above, plus lets you set buffer direction and extra sysfs buffer attributes |
Testing the Driver and Expected Output
Load the module and confirm the IIO device appeared:
$ sudo insmod ep_iio_trigbuf_demo.ko
$ ls /sys/bus/iio/devices/
iio:device0
$ cat /sys/bus/iio/devices/iio:device0/name
ep_iio_trigbuf_demo
Create an hrtimer trigger through configfs so you have a trigger source without any real hardware:
$ sudo mkdir /sys/kernel/config/iio/triggers/hrtimer/eptrig0
$ cat /sys/bus/iio/devices/trigger0/name
eptrig0
Assign the trigger to the device, enable the channels, and enable the buffer:
$ echo eptrig0 | sudo tee /sys/bus/iio/devices/iio:device0/trigger/current_trigger
$ echo 1 | sudo tee /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage0_en
$ echo 1 | sudo tee /sys/bus/iio/devices/iio:device0/scan_elements/in_voltage1_en
$ echo 1 | sudo tee /sys/bus/iio/devices/iio:device0/buffer/enable
Read continuous samples from the character device:
$ sudo hexdump -C /dev/iio:device0
00000000 00 02 84 03 e8 07 3a 91 4f 2c 8a 17 00 00 00 00
00000010 00 02 84 03 e8 07 3a 92 91 c4 a1 07 00 00 00 00
Each 16-byte record holds the two 16-bit channel values (0x0200 = 512, 0x0384 = 900) followed by the 8-byte nanosecond timestamp appended by iio_push_to_buffers_with_timestamp(). A new record arrives every time the hrtimer trigger fires.
Common Mistakes and Troubleshooting
| Symptom | Likely Cause |
|---|---|
| buffer/enable write fails with -EINVAL | No trigger assigned via current_trigger before enabling |
| Garbage or misaligned bytes in hexdump | scan_type storagebits/realbits mismatch, or missing IIO_CHAN_SOFT_TIMESTAMP channel |
| Driver hangs on module removal | Manual iio_triggered_buffer_cleanup() called in the wrong order relative to iio_device_unregister() |
| Trigger handler never runs | Forgot to build with CONFIG_IIO_TRIGGERED_BUFFER, or trigger driver module not loaded |
Best Practices
- Prefer
devm_iio_triggered_buffer_setup()over the manual variant in all new kernel 6.x drivers - Keep the top-half pollfunc as short as possible — it runs in interrupt context
- Do the real hardware read and any locking only in the bottom-half trigger handler
- Always include a timestamp channel with
IIO_CHAN_SOFT_TIMESTAMPif you plan to record capture time
Performance Considerations
Every enabled channel adds bytes to each buffer record, and every trigger fire wakes the bottom-half kernel thread. Choose a trigger rate that matches what your sensor and application actually need; an hrtimer trigger firing at a needlessly high frequency wastes CPU cycles servicing empty or redundant samples.
Security Considerations
The IIO character device under /dev/iio:deviceX is typically only readable by root or a designated group by default udev rules. If your application runs unprivileged, adjust permissions deliberately rather than opening the device world-readable, especially for sensors that could leak information about a user’s environment or activity.
Summary and Key Takeaways
- Triggered buffer support lets an IIO driver capture multiple channels together whenever a trigger fires
- Every triggered driver has a top-half pollfunc and a bottom-half trigger_handler
- iio_buffer_setup_ops callbacks start and stop hardware sampling around buffer enable/disable
- devm_iio_triggered_buffer_setup() is the modern, resource-managed way to wire this up on kernel 6.x
Conclusion
Triggered buffer support is what turns a simple IIO sysfs driver into a real streaming data source. Once you can attach a trigger, split your capture logic into a top half and bottom half, and push timestamped samples into a buffer, you have covered the core mechanism behind almost every production accelerometer, gyroscope, and ADC driver in the mainline kernel tree. The next lecture in this free Linux kernel development course builds on this foundation to cover IIO events for threshold and motion detection.
Frequently Asked Questions
What is IIO triggered buffer support used for?
It lets a Linux IIO device driver capture data from multiple channels at the same instant, whenever a trigger event fires, and deliver it to user space as a stream through a character device buffer instead of one sysfs read at a time.
What is the difference between the top half and bottom half of a trigger handler?
The top half (pollfunc) runs in interrupt context and should only record a timestamp. The bottom half (trigger_handler) runs in a kernel thread and does the actual channel reads, locking, and buffer push.
Do I need real hardware to test a triggered buffer driver?
No. The iio-trig-hrtimer trigger, created through configfs, fires on a software timer and works well for learning and testing without any interrupt-capable hardware.
Is iio_triggered_buffer_setup() still valid on kernel 6.x?
It still exists, but devm_iio_triggered_buffer_setup() is the recommended modern replacement since it ties buffer cleanup to the device lifetime automatically and removes the need for a manual remove() callback.
Why does my buffer read return extra bytes at the end of each record?
Those are the 8-byte nanosecond timestamp appended by iio_push_to_buffers_with_timestamp() when the channel array includes an IIO_CHAN_SOFT_TIMESTAMP channel.
Can one trigger drive more than one IIO device?
Yes. A trigger is a separate object from the IIO device, and multiple devices can each assign the same trigger through their current_trigger sysfs file.
What kernel config options do I need for this lecture’s example?
CONFIG_IIO, CONFIG_IIO_BUFFER, CONFIG_IIO_TRIGGERED_BUFFER, and CONFIG_IIO_HRTIMER_TRIGGER if you want to use the hrtimer trigger for testing.
| ← PREV_LEC | NEXT_LEC → |
Keep Learning Linux Kernel Development for Free
More lectures on the free Linux kernel development course, free embedded systems course, and free Linux device drivers course are on the way.
