Linux IIO 是什么
# Linux IIO 是什么
第一次看到 IIO 这个名字时,很容易误会。
它不像 VFS、netfilter、cgroup 这种名字,一看就有某种内核味。IIO 甚至还会让人以为它只是普通的 I/O。文件读写?磁盘?网络?都不是。
Linux IIO 的全称是 Industrial I/O subsystem,通常翻译成工业 I/O 子系统。它是 Linux 内核中用来支持传感器、ADC、DAC、IMU、光照、温度、压力、电流、电压等设备的一套统一框架。
换句话说,IIO 关心的是一类很具体的问题:
物理世界里有一些量,怎么稳定、统一、可扩展地进入 Linux?
比如电压是多少,温度是多少,三轴加速度是多少,陀螺仪角速度是多少。它们可能来自不同芯片,挂在不同总线上,用不同寄存器布局,但从 Linux 用户态看,最好不要每个设备都长得像一门新的方言。
IIO 就是这层翻译。
# 它解决什么问题
先想一个最普通的 ADC。
板子上有一个 ADC 芯片,通过 SPI 连到 SoC。它有 8 个输入通道,每个通道能采一个电压值。没有 IIO 的话,驱动作者当然也可以自己写一个字符设备,或者自己 invent 一套 sysfs 文件。
问题是,下一个 ADC 驱动也会这么写。再下一个温度传感器、光照传感器、加速度计也会这么写。最后用户态看到的接口会非常随缘:
这对驱动作者、应用开发者和测试工具都不友好。大家都在读传感器,结果每个设备都要重新学习一次“怎么读”。
IIO 做的事就是把这类设备的共性抽出来:
- 设备有若干个测量或输出通道。
- 通道有类型,比如 voltage、temp、accel、anglvel。
- 通道可能有 raw、scale、offset、sampling_frequency 等属性。
- 简单数据可以通过 sysfs 读取。
- 连续高速数据可以通过 buffer 从字符设备读取。
- 采样可以由 trigger 触发。
所以 IIO 不是为了某一个芯片服务的。它是为了让这一类设备在 Linux 里有统一的形状。
# IIO 设备长什么样
一个 IIO 设备在用户态通常会出现在这里:
如果它是一个 ADC,你可能会看到:
如果它是一个三轴加速度计,你可能会看到:
这些名字背后有一套约定。in_ 表示输入,voltage 表示电压类型,0 表示通道编号,raw 表示原始值,scale 表示换算比例。
用户态要拿到真实电压,通常不是只读一个文件,而是读 raw 和 scale:
然后计算:
有些通道还会有 offset,于是公式会变成:
这就是 IIO 很重要的一个设计习惯:驱动尽量把硬件原始数据和换算信息都暴露出来,用户态可以明确知道自己拿到的是原始采样,还是已经处理过的物理量。
# 四个核心概念
理解 IIO,我觉得抓住四个词就够入门了。
第一个是 device。
一个 iio:deviceX 通常对应一个实际设备,比如一个 ADC、一个 IMU、一个温度传感器。内核里对应的核心结构是 struct iio_dev。它描述这个设备叫什么、有哪些通道、支持哪些操作。
第二个是 channel。
channel 是 IIO 的灵魂。一个 ADC 的 voltage0 是一个 channel,voltage1 也是一个 channel。一个三轴加速度计有 accel_x、accel_y、accel_z 三个 channel。内核里常用 struct iio_chan_spec 描述通道。
第三个是 buffer。
sysfs 适合偶尔读一下当前值,但不适合高频连续采样。如果你要以固定频率读取三轴加速度,并且每次还要带时间戳,靠 cat 一堆 sysfs 文件就太粗糙了。
IIO buffer 用来做连续采样。用户态可以选择哪些 channel 进入 scan,然后从 /dev/iio:deviceX 读取一帧一帧的数据。这样可以减少系统调用和 CPU 开销,也更适合高采样率场景。
第四个是 trigger。
trigger 决定什么时候采样。它可以来自设备自己的 data-ready 中断,可以来自定时器,也可以来自外部 GPIO。IIO 里 buffer 和 trigger 经常一起出现:trigger 触发一次,驱动采一组数据,推到 buffer,用户态再读取。
用一句话串起来:
IIO device 由多个 channel 组成;简单值通过 sysfs 暴露;连续数据进入 buffer;什么时候采样由 trigger 决定。
# IIO 和 hwmon、input、GPIO 有什么区别
这个问题很容易混。
hwmon 也能读温度、电压、风扇转速。它更偏向硬件监控,比如主板温度、电源电压、风扇状态。它的接口很适合“系统监控”。
input 也能处理某些传感器,尤其是会产生用户输入语义的设备,比如键盘、鼠标、触摸屏、某些开关。它关心的是事件。
GPIO 处理的是通用数字引脚,高低电平、方向、中断。它不是为了表达“这个通道的物理量是多少”。
IIO 的气质不一样。它更像是“采样数据框架”。它面向的是 ADC/DAC、传感器和工业测量设备,关注 channel、采样频率、scale、offset、buffer、trigger、timestamp。
如果一个设备的核心价值是连续或按需采集物理量,那它大概率应该先考虑 IIO。
# 驱动开发时 IIO 帮你做什么
从驱动角度看,IIO 的价值不只是“文件名统一”。
一个 IIO 驱动通常要做几件事:
- 分配并注册
iio_dev。 - 描述设备有哪些 channel。
- 实现读取 raw、scale、offset 等信息的回调。
- 如果支持连续采样,配置 buffer 和 trigger。
- 在触发时读取硬件数据,并推入 IIO buffer。
非常简化地看,一个驱动会把通道描述成这样:
这段代码表达的不是某个奇怪文件路径,而是一个稳定语义:
这个设备有一个电压输入通道 0,它支持读取 raw 值,同类型通道共享 scale。
IIO core 会基于这些描述帮你生成用户态接口。驱动作者不用给每个芯片重新设计一套用户态协议。
# 一个最小用户态视角
如果你在一块 Linux 板子上调传感器,可以先这样看:
找到设备后看名字:
再看它有哪些属性:
如果只是读一次值:
如果你看到 buffer0、scan_elements、trigger 这些目录,那说明这个设备可能支持更完整的连续采样路径。
典型流程会是:
真实项目里一般不会直接 cat /dev/iio:device0,因为 buffer 里的数据有类型、字节序、对齐和 scan index。用户态程序需要根据 scan_elements/*_type 和 *_index 解析。这里的重点是:IIO 已经把“配置采样”和“读取数据流”的路径标准化了。
# 为什么它叫 Industrial I/O
名字里的 Industrial 很容易让人以为它只属于工厂、PLC、工业现场。
历史上它确实和工业测量、数据采集这类场景很贴近,但今天 IIO 覆盖的设备已经很广。手机、开发板、机器人、无人机、边缘设备上常见的传感器,也经常通过 IIO 暴露。
所以不要被名字限制住。只要你的设备像是在读写物理世界里的连续量,IIO 就可能是它在 Linux 里的家。
# 结尾
我会把 IIO 理解成 Linux 内核里的“物理量入口”。
它不关心你的 ADC 是哪家厂商,也不关心你的 IMU 通过 I2C 还是 SPI 连接。它更关心这些问题:
- 这个设备有哪些通道?
- 每个通道是什么类型?
- 读到的是 raw 值还是 processed 值?
- scale 和 offset 怎么换算?
- 要不要连续采样?
- 采样由谁触发?
- 用户态如何稳定地拿到数据?
这就是 IIO 的意义。
它让内核驱动不必各说各话,让用户态不必为每个传感器重新猜接口,也让“从物理世界到 Linux 应用”的那条路变得规矩一点。
写驱动时,IIO 是框架;调板子时,IIO 是入口;理解系统时,IIO 是 Linux 如何认识传感器世界的一层抽象。
