IIO Buffer Setup and Device Registration- Free Linux Device Drivers Tutorial

IIO Buffer Setup and Device Registration
Part 3 of the Linux IIO Framework series — free Linux kernel development course by EmbeddedPathashala
Chapter 10 · Lecture 3
Kernel 6.x Ready
Original Driver Code

iio buffer setup linux kernel
free linux device drivers course
free linux kernel development course
free embedded linux course
iio_device_register

Getting IIO buffer setup right is what separates a driver that only exposes raw sysfs values from one that can stream real sensor data to user space at speed. In this lecture of our free Linux device drivers course, we pick up right where Part 2 left off and open up the fields inside struct iio_dev that control triggered buffers, then walk through how an IIO device is actually allocated and registered with the kernel, and finally introduce the iio_info and iio_chan_spec structures that every IIO driver depends on.

Everything here targets a current 6.x mainline kernel, uses original example code written for this course, and avoids the outdated APIs you’ll still find in older textbooks.

What You Will Learn

  • Every field inside iio_dev that supports triggered buffer capture
  • How iio_buffer_setup_ops hooks into the enable/disable lifecycle
  • The modern, devm-managed way to allocate and register an IIO device
  • What changed in struct iio_info on recent kernels
  • The first fields of iio_chan_spec and why channels matter
  • How to build, load, and test a working IIO driver on a real board or QEMU

Prerequisites

  • Part 1 and Part 2 of this IIO Framework series (device/channel model, sysfs vs chardev, read_raw/write_raw)
  • Comfort writing and building an out-of-tree kernel module
  • Basic familiarity with sysfs attributes and devm_* managed resources

Recap: From Single Values to Buffered Capture

In Part 1 we built a driver that returned one voltage sample per read_raw() call. That’s fine for a slow-changing value, but most real sensors — accelerometers, gyroscopes, ADCs sampling at kilohertz rates — need to stream many samples continuously. That’s exactly what IIO’s triggered buffer mode is for, and it’s controlled by a set of fields living inside iio_dev that we’ll unpack next.

Fields That Drive IIO Buffer Setup

The table below summarizes the fields you’ll touch when wiring up triggered buffer support in your driver. This is the core of IIO buffer setup linux kernel work, so take your time here.

Field Purpose
buffer The kfifo/dmaengine data buffer pushed to user space; allocated automatically once you call iio_triggered_buffer_setup()
scan_bytes Total bytes captured per scan cycle; the user-space read buffer must be at least this large
available_scan_masks An optional list of the channel-enable combinations your hardware actually supports
active_scan_mask A bitmask showing which channels are currently enabled for capture
scan_timestamp Whether a 64-bit timestamp is appended as the last element of every scan
trig The trigger currently driving this device, when buffer mode is active
pollfunc The function invoked each time the trigger fires
channels / num_channels The channel specification table and its length
setup_ops Callbacks run around buffer enable/disable — see below

available_scan_masks in Practice

Say you’re driving a 3-axis accelerometer with X, Y, and Z channels. If your hardware can only capture all three axes together or none at all, you’d restrict the mask so users can’t cherry-pick just X and Y. In bit terms, that means allowing 0b111 (all three) and 0b000 (none), and rejecting anything in between.

The iio_buffer_setup_ops Callbacks

Whenever a triggered buffer is enabled or disabled from user space, the IIO core calls back into your driver through setup_ops:

  • preenable — runs before the buffer starts; good place to power up hardware
  • postenable — runs after buffer state is ready; often used to attach the poll function
  • predisable — runs before the buffer is torn down
  • postdisable — runs after teardown; a natural place to power hardware back down
  • validate_scan_mask — lets you reject a scan mask combination your hardware can’t honor

If you don’t supply setup_ops at all, the IIO core falls back to its own default triggered-buffer setup ops, which is enough for the simplest drivers.

IIO Triggered Buffer Lifecycle
preenable
power up hardware
postenable
attach poll function
buffer streaming
samples pushed to user space
predisable
detach poll function
postdisable
power hardware down

Allocating and Registering an IIO Device

Before any of the fields above matter, you need a live iio_dev instance. On a modern kernel the resource-managed helper is what you want almost every time:

struct my_sensor_priv {
    struct mutex lock;
    int gain;
};

static int my_sensor_probe(struct i2c_client *client)
{
    struct iio_dev *indio_dev;
    struct my_sensor_priv *priv;

    indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*priv));
    if (!indio_dev)
        return -ENOMEM;

    priv = iio_priv(indio_dev);
    mutex_init(&priv->lock);

    indio_dev->name = "my_sensor";
    indio_dev->info = &my_sensor_info;
    indio_dev->channels = my_sensor_channels;
    indio_dev->num_channels = ARRAY_SIZE(my_sensor_channels);
    indio_dev->modes = INDIO_DIRECT_MODE;

    return devm_iio_device_register(&client->dev, indio_dev);
}

devm_iio_device_alloc() reserves memory for both the iio_dev structure and your own private data block in one call, and iio_priv() hands back a pointer to that private block. Because both calls are devm_-managed, the kernel tears everything down automatically when the device is removed — you don’t write a matching remove() cleanup path at all for these two steps.

Legacy vs Modern Allocation and Registration

Step Legacy Approach Modern Kernel 6.x Approach
Allocate iio_device_alloc(sizeof_priv), manual iio_device_free() in remove devm_iio_device_alloc(dev, sizeof_priv), auto-freed
Register iio_device_register(indio_dev), manual iio_device_unregister() in remove devm_iio_device_register(dev, indio_dev), auto-unregistered
Error handling Manual unwind ordering required Handled by the devm core in reverse order

Tip: The plain (non-devm) iio_device_alloc() and iio_device_register() calls still exist and still work, but every new driver merged into mainline today uses the devm variants unless there’s a very specific reason not to.

The iio_info Structure on Current Kernels

Every IIO device points to one iio_info structure, which the core uses to find your driver’s callbacks:

static const struct iio_info my_sensor_info = {
    .read_raw  = my_sensor_read_raw,
    .write_raw = my_sensor_write_raw,
};

If you’ve seen older training material reference a .driver_module = THIS_MODULE line inside this structure, skip it — that field was removed from struct iio_info years ago and no longer exists on any supported kernel. Trying to set it will fail to build.

  • attrs — an optional sysfs attribute group for extra device attributes
  • read_raw — called when user space reads a channel attribute; the mask parameter tells you whether the raw value, scale, or something else is being requested
  • write_raw — the write-side counterpart, used for things like setting sampling frequency or gain

Introducing iio_chan_spec

Every entry in your channels array is one iio_chan_spec describing a single acquisition line — an accelerometer with X, Y, and Z axes has three of these. The fields you’ll use immediately are:

  • type — the physical quantity, such as IIO_VOLTAGE or IIO_ACCEL
  • channel — the index number assigned to this channel
  • channel2 — a second index, used for differential channels
  • scan_index — position of this channel’s data inside a triggered buffer scan
  • info_mask_separate — which per-channel attributes (raw, scale, and so on) are exposed to sysfs

A minimal single-channel voltage spec looks like this:

static const struct iio_chan_spec my_sensor_channels[] = {
    {
        .type = IIO_VOLTAGE,
        .indexed = 1,
        .channel = 0,
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
                               BIT(IIO_CHAN_INFO_SCALE),
    },
};

We’ll go much deeper into iio_chan_spec — including scan_type, the full info_mask_* family, and modifiers — in the next lecture.

Build and Test Walkthrough

Compile and load your module against a running 6.x kernel tree, then confirm registration:

make -C /lib/modules/$(uname -r)/build M=$PWD modules
sudo insmod my_sensor.ko
dmesg | tail -n 5
ls /sys/bus/iio/devices/

Expected dmesg output on a successful probe:

my_sensor 1-0048: iio device registered as iio:device0
my_sensor 1-0048: probe complete

And you should see a new entry such as iio:device0 under /sys/bus/iio/devices/, with in_voltage0_raw and in_voltage0_scale attribute files inside it.

Common Mistakes and Troubleshooting

Mistake Symptom Fix
Forgetting to set indio_dev->info Null pointer dereference on first sysfs read Always assign info before calling iio_device_register()
Using plain iio_device_alloc() without a matching free Memory leak on module unload Switch to devm_iio_device_alloc()
Copying old .driver_module line into iio_info Build failure — no such member Remove the field; it isn’t needed on modern kernels
Mismatched scan_bytes and actual buffer size Truncated or garbled samples in user space Size the read buffer to at least indio_dev->scan_bytes

Best Practices

  • Prefer devm_iio_device_alloc() and devm_iio_device_register() for new drivers
  • Keep iio_chan_spec tables static const — they never change at runtime
  • Use available_scan_masks whenever your hardware can’t capture arbitrary channel combinations
  • Protect any shared private state with a mutex, as shown in earlier lectures of this course

Performance Considerations

Buffered, trigger-driven capture is far more CPU-efficient than polling sysfs in a tight user-space loop, since samples are pushed to a kernel-side buffer and read in bulk. Keep your pollfunc handler short — heavy work there adds latency to every single trigger event.

Security Considerations

Validate any scan mask or channel combination requested from user space inside validate_scan_mask rather than trusting it blindly; a malformed or unsupported mask should be rejected, not silently truncated.

Summary and Key Takeaways

  • iio_dev carries dedicated fields — buffer, scan_bytes, active_scan_mask, trig, pollfunc — purely for triggered buffer capture
  • setup_ops gives you five lifecycle hooks around buffer enable/disable
  • Modern IIO buffer setup relies on devm_iio_device_alloc() and devm_iio_device_register() instead of manual alloc/free pairs
  • struct iio_info no longer has a driver_module field on current kernels
  • iio_chan_spec is where each channel’s identity and sysfs exposure is declared

Conclusion

With the buffer-related fields of iio_dev, the modern allocation and registration flow, and the first layer of iio_chan_spec now covered, you have everything needed to register a working, modern IIO device. The next lecture in this free Linux kernel development course goes deep into the rest of iio_chan_spec — scan types, the full info_mask family, and channel modifiers — so you can describe real multi-channel hardware precisely.

Frequently Asked Questions

What is IIO buffer setup used for in Linux drivers?

It lets a driver stream many samples per trigger event into a kernel buffer that user space reads in bulk, instead of one value per sysfs read.

Do I need setup_ops for every IIO driver?

No. If you don’t supply it, the IIO core uses a sensible default triggered-buffer setup, which is enough for simple devices.

Is iio_device_alloc() still valid on kernel 6.x?

Yes, it still compiles and works, but new drivers should use the devm-managed devm_iio_device_alloc() so cleanup is automatic.

Why did my driver fail to build with a driver_module error?

That field was removed from struct iio_info years ago. Delete the line — it isn’t part of the current struct.

What does active_scan_mask actually contain?

A bitmask where each set bit corresponds to one channel currently enabled for buffered capture.

What is iio_priv() for?

It returns a pointer to the private data block reserved alongside the iio_dev when you called devm_iio_device_alloc(), so you can store per-device state.

Can I mix legacy and devm-managed IIO calls in one driver?

Technically yes, but it’s easy to create mismatched cleanup paths. Stick to one approach — devm-managed is recommended.

What is scan_index used for in iio_chan_spec?

It defines where a channel’s data lands inside the buffer during a triggered scan, which matters once you have more than one channel.

Where can I try this without real hardware?

You can build and load the demo driver in a QEMU virtual machine running a 6.x kernel with the IIO subsystem enabled, and test sysfs attributes the same way as on real hardware.

What comes after this lecture in the course?

The next lecture covers the remaining iio_chan_spec fields in depth — scan_type, the info_mask_* family, and channel modifiers — with a new original driver example.

Continue the Free Linux Kernel Development Course

More IIO framework lectures, device driver tutorials, and Bluetooth stack guides — all free on EmbeddedPathashala.

Leave a Reply

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