1. 课程导入

Linux 设备模型是内核中连接总线、设备与驱动三者的核心抽象层,它决定了设备如何被发现、匹配、绑定、供电、挂载与卸载。从 2.6 内核引入统一设备模型以来,kobject、kset、device、driver、bus_type、class 等结构不断演进,设备树(Device Tree)、ACPI、device_link、fwnode、auxiliary_bus、component 框架等机制相继加入。同一份驱动代码,在 4.19、5.4、5.10、5.15、6.1、6.6 等 LTS 版本之间迁移时,常常出现编译失败、匹配不上、probe 顺序错乱、电源域失控、热插拔异常等问题。本课程正是围绕“跨版本适配”这一工程痛点,建立一套可复用的分析、改造与验证方法。

一、学习目标:从“能编译”到“能稳定运行”

本课程不满足于让驱动在某个内核上“编过”,而是追求在多个 LTS 版本上行为一致、可维护、可回归。具体目标包括:

  • 理解设备模型的核心数据结构及其跨版本语义变化,能读懂 include/linux/device.h 与 drivers/base/ 的差异。
  • 掌握总线匹配、probe 时机、device_link 依赖、fwnode 抽象等关键机制的版本差异。
  • 能够使用 compatible、of_match_table、acpi_match_table、auxiliary_device 等手段完成设备与驱动的跨版本绑定。
  • 建立“版本矩阵 + 条件编译 + 兼容层”的工程化适配策略,而不是到处写 #ifdef。
  • 掌握用 sysfs、debugfs、dev_dbg、ftrace、gpioinfo 等工具定位适配问题的能力。

二、核心挑战:版本差异到底差在哪里

跨版本适配的困难,本质上是“接口语义漂移”与“生命周期时序变化”的叠加。常见挑战可归纳为以下几类:

挑战类型 典型表现 常见版本区间
API 签名变化 platform_driver.remove 返回值由 int 变为 void 5.18 前后
匹配机制变化 of_match_table 与 acpi_match_table 优先级、fwnode 统一 5.10~6.1
电源管理变化 dev_pm_domain、genpd、runtime_pm 默认行为调整 5.4~6.6
设备链接变化 device_link_add 标志位与消费者/供应者语义 4.19~5.15
热插拔与并发 probe 异步化、deferred_probe 队列行为差异 5.10 以后

最常见的误区是:只关注编译错误,忽略运行时行为差异。很多适配问题在编译期完全无感,却会在 probe 顺序、电源域释放、热插拔重入时集中爆发。

三、整体知识框架:三层五域

为便于工程落地,本课程将知识体系组织为“三层五域”。三层是:数据结构层(kobject/device/driver/bus)、机制层(匹配、probe、电源、链接、热插拔)、工程层(版本矩阵、兼容层、CI 回归)。五域是:总线与匹配、设备树与 ACPI、电源管理、设备链接与依赖、调试与验证。下图给出整体关系:

Linux 设备模型跨版本适配知识框架 数据结构层 kobject · kset · device · device_driver · bus_type · class · fwnode 关注:结构体字段增删、引用计数语义、生命周期回调变化 机制层 总线匹配 · probe 时机 · 设备树/ACPI 解析 · 电源管理 · device_link · 热插拔 关注:匹配优先级、deferred probe、genpd 默认行为、链接标志位 关键版本:4.19 / 5.4 / 5.10 / 5.15 / 6.1 / 6.6 工程层 版本矩阵 · 兼容层 · 条件编译策略 · CI 回归 · 日志与追踪 关注:可维护性、可测试性、可回滚性 总线与匹配 of/acpi/auxiliary compatible 优先级 设备树与 ACPI fwnode 统一抽象 属性解析差异 电源与链接 genpd / runtime_pm device_link 依赖 调试与验证 sysfs / ftrace CI 版本矩阵

四、实战路线图:从单版本到多版本

课程按“先建立认知、再动手改造、最后工程化”的节奏推进,建议按以下路线执行:

  1. 基线确认:选定目标 LTS 版本集合,例如 5.4、5.10、5.15、6.1,记录各版本 uname -r 与配置。
  2. 差异扫描:用 git log、git diff 对比 drivers/base/ 与 include/linux/device.h,列出影响本驱动的变更点。
  3. 兼容层设计:把差异收敛到 compat.h 或独立适配文件,避免污染业务逻辑。
  4. 逐版本验证:在每个版本上执行编译、加载、probe、卸载、热插拔、电源切换六项检查。
  5. 回归固化:将验证脚本接入 CI,形成版本矩阵回归,防止后续修改引入回退。

建议在项目根目录维护一份 VERSIONS.md,记录每个内核版本上已验证的驱动版本、配置项与已知问题,作为团队共享的适配基线。

五、一个典型场景:platform 驱动从 5.4 迁移到 6.1

假设某 GPIO 控制器驱动在 5.4 上工作正常,迁移到 6.1 后出现 probe 失败。排查时可按以下顺序进行:

# 1. 查看设备是否被创建
ls /sys/bus/platform/devices/ | grep gpio

# 2. 查看驱动是否注册
ls /sys/bus/platform/drivers/ | grep gpio

# 3. 查看匹配与 probe 日志
dmesg | grep -i "gpio\|probe\|deferred"

# 4. 检查设备树 compatible 是否命中
cat /proc/device-tree/soc/gpio@10000000/compatible

# 5. 查看 deferred probe 队列
cat /sys/kernel/debug/devices_deferred

常见根因包括:of_match_table 中 compatible 字符串与设备树不一致;remove 回调签名未随 5.18 变更调整;devm_gpiochip_add_data 返回码处理差异;电源域未在 6.1 上正确声明导致 -EPROBE_DEFER 循环。通过“设备—驱动—匹配—probe—电源”五步定位法,可以快速缩小范围。

跨版本适配的核心心法:把版本差异当作外部依赖来管理,而不是散落在代码各处的条件编译。差异越集中,维护成本越低,回归越可靠。