IIO Driver Probe Function- Free Linux Device Drivers Tutorial

« PREV_LEC  |  NEXT_LEC »

IIO Driver Probe Function

Free Linux Kernel Development Course — IIO Framework, Lecture 10

Level
Intermediate
Kernel
6.x (current)
Reading Time
18-20 min

If you have been following this free linux kernel development course, you already know how read_raw() and write_raw() work in isolation. This lecture closes the loop: it walks through the iio driver probe function that ties struct iio_info, your channel array, and the IIO core together into a device that actually shows up under /sys/bus/iio/devices/. We build an original driver, ep_iio_probe_demo, from an empty probe() to a fully working sysfs interface, on a modern 6.x kernel.

What You Will Learn

  • How struct iio_info connects sysfs reads/writes to your driver code
  • The exact meaning of the mask parameter in read_raw() and write_raw()
  • Step-by-step assembly of an iio driver probe function using devm_iio_device_alloc() and devm_iio_device_register()
  • Why modern IIO drivers no longer need a manual remove() callback
  • What available_scan_masks does and when you actually need it
  • How to test a freshly probed IIO device from the command line

Prerequisites

  • Lecture 1-2 of this chapter: the IIO channel model and iio_chan_spec basics
  • Comfort reading basic SPI or I2C client driver code
  • A Linux kernel tree at version 6.1 or newer for building and testing
free embedded systems course
free linux device drivers course
free linux kernel development course
free embedded linux course

Why the Probe Function Matters

Every earlier lecture in this chapter dealt with one piece of an IIO driver: a channel spec here, a read hook there. None of those pieces do anything on their own. The probe() function is where a driver author allocates the iio_dev object, fills in its fields, and hands it to the IIO core with a single registration call. Get the field values wrong and either the device never appears, or it appears with missing sysfs files.

From Probe to Sysfs
bus match
SPI/I2C core calls probe()
devm_iio_device_alloc()
reserve iio_dev + private data
fill iio_dev fields
name, info, channels, modes
devm_iio_device_register()
sysfs files created

struct iio_info: The Hook Table

struct iio_info is a small table of function pointers that the IIO core calls whenever user space touches a sysfs attribute. You only implement the hooks your device actually needs.

Field Called when… Required?
read_raw user space reads a *_raw, *_scale, or similar attribute Almost always
write_raw user space writes to a settable attribute such as *_scale Only if a channel is settable
attrs the driver exposes custom, non-channel sysfs attributes Optional
read_avail user space reads an *_available list (valid scale/frequency values) Recommended if write_raw is settable

Note the field you will not find here on a current kernel: driver_module. Older reference material still shows it, but it was removed from struct iio_info years ago because the core now derives ownership from the parent device. Setting it today is a compile error, not a warning.

Reading the mask Parameter Correctly

Both hooks receive a long mask argument. This single value tells you which sysfs attribute triggered the call, so one function can serve every attribute of a channel.

int (*read_raw)(struct iio_dev *indio_dev,
                 struct iio_chan_spec const *chan,
                 int *val, int *val2, long mask);
mask value Sysfs attribute Output convention
IIO_CHAN_INFO_RAW in_voltage0_raw *val = raw ADC/register count
IIO_CHAN_INFO_SCALE in_voltage0_scale *val, *val2 = integer + micro-fraction multiplier
IIO_CHAN_INFO_OFFSET in_voltage0_offset value added before applying scale

The physical reading is always (raw + offset) * scale. Splitting raw and scale keeps the fast path (a raw register read) cheap, while scale conversion stays in floating-point-free integer math that user space or a library like libiio can apply.

Building ep_iio_probe_demo

To keep the example focused, ep_iio_probe_demo models a small SPI voltage sensor with one channel and a settable gain. It is original code written for this course, not copied from any book or repository.

struct ep_probe_data {
	struct spi_device *spi;
	struct mutex lock;
	int gain_idx;
};

static const int ep_gain_table[4][2] = {
	{ 1, 0 },       /* x1   */
	{ 2, 0 },       /* x2   */
	{ 4, 0 },       /* x4   */
	{ 8, 0 },       /* x8   */
};

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

Next, the read_raw() and write_raw() hooks. read_raw() answers both IIO_CHAN_INFO_RAW and IIO_CHAN_INFO_SCALE; write_raw() only handles IIO_CHAN_INFO_SCALE, letting user space pick a gain step.

static int ep_probe_read_raw(struct iio_dev *indio_dev,
			      struct iio_chan_spec const *chan,
			      int *val, int *val2, long mask)
{
	struct ep_probe_data *data = iio_priv(indio_dev);
	u8 tx = 0x80, rx[2];
	int ret;

	switch (mask) {
	case IIO_CHAN_INFO_RAW:
		mutex_lock(&data->lock);
		ret = spi_write_then_read(data->spi, &tx, 1, rx, 2);
		mutex_unlock(&data->lock);
		if (ret < 0)
			return ret;
		*val = (rx[0] << 8) | rx[1]; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = ep_gain_table[data->gain_idx][0];
		*val2 = ep_gain_table[data->gain_idx][1];
		return IIO_VAL_INT;
	}
	return -EINVAL;
}

static int ep_probe_write_raw(struct iio_dev *indio_dev,
			       struct iio_chan_spec const *chan,
			       int val, int val2, long mask)
{
	struct ep_probe_data *data = iio_priv(indio_dev);
	int i;

	if (mask != IIO_CHAN_INFO_SCALE)
		return -EINVAL;

	for (i = 0; i < ARRAY_SIZE(ep_gain_table); i++) { if (val == ep_gain_table[i][0] && val2 == ep_gain_table[i][1]) { mutex_lock(&data->lock);
			data->gain_idx = i;
			mutex_unlock(&data->lock);
			return 0;
		}
	}
	return -EINVAL;
}

static const struct iio_info ep_probe_info = {
	.read_raw  = ep_probe_read_raw,
	.write_raw = ep_probe_write_raw,
};

Assembling iio_dev in probe()

This is the part earlier lectures only touched briefly. Every field you set here has a direct, visible effect on the sysfs directory the IIO core creates.

static int ep_probe(struct spi_device *spi)
{
	struct iio_dev *indio_dev;
	struct ep_probe_data *data;

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

	data = iio_priv(indio_dev);
	data->spi = spi;
	mutex_init(&data->lock);
	data->gain_idx = 0;

	indio_dev->name = "ep_iio_probe_demo";
	indio_dev->info = &ep_probe_info;
	indio_dev->modes = INDIO_DIRECT_MODE;
	indio_dev->channels = ep_probe_channels;
	indio_dev->num_channels = ARRAY_SIZE(ep_probe_channels);

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

static const struct spi_device_id ep_probe_id[] = {
	{ "ep-iio-probe-demo", 0 },
	{ }
};
MODULE_DEVICE_TABLE(spi, ep_probe_id);

static struct spi_driver ep_probe_driver = {
	.driver = {
		.name = "ep_iio_probe_demo",
	},
	.probe    = ep_probe,
	.id_table = ep_probe_id,
};
module_spi_driver(ep_probe_driver);
iio_dev field What it controls
name text you see in cat /sys/bus/iio/devices/iio:deviceN/name
info which read_raw/write_raw pair services this device
modes whether direct sysfs reads, buffered capture, or both are allowed
channels / num_channels which sysfs attribute files get generated

Modernization note: notice there is no ep_probe_remove() function and no call to iio_device_unregister(). Because the driver used devm_iio_device_alloc() and devm_iio_device_register(), the device-managed (devm_) core automatically unregisters and frees everything when the SPI device is removed. Older reference material built around iio_device_alloc() / iio_device_register() still needs a manual remove() with the matching free/unregister calls — write new drivers with the devm_ variants instead.

A Note on available_scan_masks

If your device supports buffered capture across multiple channels (covered in earlier lectures of this chapter), iio_dev also has an available_scan_masks field: an array of bitmasks describing which channel combinations the hardware can sample together. A single-channel, non-buffered driver like ep_iio_probe_demo can leave it unset. A three-axis sensor that can only be read all-at-once or not-at-all would set it once, in probe, as a small static array — the same idea you saw applied to buffer setup in the earlier trigger lectures.

Testing the Driver

Build and load the module on a target or QEMU instance with SPI support, then inspect the generated sysfs tree.

$ sudo insmod ep_iio_probe_demo.ko
$ ls /sys/bus/iio/devices/iio:device0/
in_voltage0_raw  in_voltage0_scale  name  ...

$ cat /sys/bus/iio/devices/iio:device0/name
ep_iio_probe_demo

$ cat /sys/bus/iio/devices/iio:device0/in_voltage0_raw
1832

$ cat /sys/bus/iio/devices/iio:device0/in_voltage0_scale
2.000000

$ echo "4 0" | sudo tee /sys/bus/iio/devices/iio:device0/in_voltage0_scale
$ cat /sys/bus/iio/devices/iio:device0/in_voltage0_scale
4.000000

The real voltage is raw * scale in millivolts (or whatever unit your datasheet defines). Changing the scale here changes the gain register on the next read.

Common Mistakes

Mistake Symptom Fix
Forgetting indio_dev->info Device registers but every sysfs read returns -ENODEV Always set info before calling register
Mismatched info_mask_separate bits Expected sysfs file is missing Add the matching IIO_CHAN_INFO_* bit for every attribute you implement
Using iio_device_register() with a manual remove() that forgets to unregister Kernel warning or crash on module unload Prefer the devm_ alloc/register pair
Setting driver_module in iio_info Build fails on kernel 5.x/6.x Remove the field entirely — it no longer exists

Best Practices

  • Always take a lock around hardware transactions inside read_raw/write_raw — sysfs reads can happen concurrently
  • Return -EINVAL for any unhandled mask value instead of silently ignoring it
  • Prefer devm_ allocation and registration functions for new drivers on kernel 6.x
  • Keep probe() focused on wiring fields together; push hardware setup into small helper functions

Performance and Security Considerations

Every sysfs read triggers a fresh call into read_raw() — there is no built-in caching, so a driver that talks to slow hardware over I2C should keep the bus transaction as short as possible and consider buffered/triggered capture (covered earlier in this chapter) for high sample-rate use cases. On the security side, always validate values arriving through write_raw() against a known table (as ep_probe_write_raw does) rather than writing raw user-supplied numbers straight to a hardware register.

Summary / Key Takeaways

  • struct iio_info is the hook table; read_raw/write_raw are dispatched by the mask parameter
  • The iio driver probe function allocates with devm_iio_device_alloc(), fills name/info/modes/channels, and finishes with devm_iio_device_register()
  • devm_ variants remove the need for a manual remove() callback
  • driver_module no longer exists in struct iio_info on current kernels

Conclusion

With this lecture, you can now read an IIO driver end to end: channel specs describe what exists, iio_info describes how to read and write it, and probe() is the glue that registers the whole thing with the kernel. That is the complete mental model behind every IIO client driver you will encounter in mainline Linux, and the same pattern ep_iio_probe_demo uses here scales up cleanly to the multi-channel and buffered drivers built earlier in this chapter.

FAQ

What is the difference between read_raw and write_raw in an IIO driver?

read_raw() answers sysfs reads (raw values, scale, offset); write_raw() handles writes to settable attributes such as scale or calibration bias. Both share the same mask convention.

Do I need to implement write_raw for every IIO driver?

No. Only implement it for channels where a value is meant to be user-adjustable, such as a settable gain or sampling frequency. A read-only sensor only needs read_raw.

Why did my driver fail to build with an error about driver_module?

driver_module was removed from struct iio_info. Reference material written against older kernels still shows it; delete that line from your driver and it will build cleanly on kernel 6.x.

Do I still need a remove() function in a modern IIO driver?

Not if you used devm_iio_device_alloc() and devm_iio_device_register(). The device-managed core unregisters and frees the device automatically when the parent device is removed.

What does the mask parameter in read_raw actually represent?

It identifies which sysfs attribute triggered the call, using constants like IIO_CHAN_INFO_RAW or IIO_CHAN_INFO_SCALE, so a single function can serve multiple attributes of the same channel.

When do I need to set available_scan_masks in iio_dev?

Only when the device supports buffered, multi-channel capture and hardware constraints limit which channel combinations can be sampled together. Single-channel, direct-mode drivers can leave it unset.

How is the real-world value calculated from raw and scale?

The IIO convention is (raw + offset) * scale. Not every channel implements offset; when it is absent, treat it as zero.

Continue the Free Linux Kernel Development Course

More IIO framework lectures, character device drivers, and platform driver tutorials are available on EmbeddedPathashala.

« PREV_LEC  |  NEXT_LEC »

 

Leave a Reply

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