IIO Triggered Buffer Support Guide- Free Linux Device Drivers Tutorial

IIO Triggered Buffer Support Guide

Free Linux Kernel Development Course — Chapter 10, Lecture 6

Kernel 6.x Verified
Original Driver Code
Hands-On Testing
← PREV_LEC NEXT_LEC →
Free Linux Kernel Development Course
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_ops lifecycle 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, and CONFIG_IIO_TRIGGERED_BUFFER enabled

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.

Triggered Capture Flow

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.

Buffer Setup Ops Sequence
preenable

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_TIMESTAMP if 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.

Leave a Reply

Your email address will not be published. Required fields are marked *