2、Platform驱动框架概述:Platform总线、设备与驱动的匹配机制,为什么需要Platform框架
2.1 从一次“硬编码”的教训说起
我记得刚入行那会儿,接手一个老项目。
板子上挂了个GPIO按键,驱动里直接写死了中断号——IRQ 42。当时觉得挺正常,嵌入式嘛,硬件固定,写死就写死了。结果产品迭代,换了颗主控,中断号变成了56。好家伙,整个驱动得重新编译,还得改好几处代码。
你想想看,这还只是一个按键。要是换成I2C控制器、SPI设备、网卡……每个都这么搞,那代码得乱成什么样?
说白了,传统驱动开发方式就是“硬件信息硬编码”。驱动和设备紧紧耦合在一起。换一块板子,驱动就得改。这显然不是个可持续的方案。
核心痛点:驱动代码里混杂了硬件地址、中断号、DMA通道等平台相关参数。导致驱动无法跨平台复用,维护成本极高。
2.2 Platform框架的诞生:解耦的艺术
Linux内核社区也意识到了这个问题。他们想:能不能把“设备信息”和“驱动逻辑”分开?
设备信息归设备信息,比如基地址0x1000、中断号56。驱动逻辑归驱动逻辑,比如怎么初始化、怎么收发数据。两者通过一个“中间人”来牵线搭桥。
这个中间人,就是Platform总线。
Platform总线是Linux内核中一种虚拟总线。它不像PCI、USB那样有真实的物理总线,而是用来管理那些“挂载在CPU总线上、但无法被自动枚举”的设备。比如:
- 片上外设(UART、I2C、SPI控制器)
- GPIO控制器
- 中断控制器
- 一些简单的内存映射设备
我习惯把Platform总线理解成一个“婚姻介绍所”。
设备端说:“我叫uart1,我在地址0x1000,中断号56。”
驱动端说:“我能驱动uart,我的名字叫uart_driver。”
Platform总线负责匹配,匹配上了,就调用驱动的probe函数,把设备信息传进去。
这样一来,驱动里不再有硬编码的地址和中断号。所有硬件参数都从设备信息里拿。换板子?改设备信息就行,驱动代码不用动。
2.3 匹配机制:三种“相亲”方式
Platform总线怎么匹配设备和驱动?主要有三种方式。我在项目中三种都遇到过,各有各的适用场景。
| 匹配方式 | 匹配依据 | 典型场景 |
|---|---|---|
| 设备树匹配 | compatible 属性 | 现代ARM/ RISC-V平台,主流方式 |
| ACPI匹配 | ACPI表项 | x86平台,服务器领域 |
| Legacy ID匹配 | name 字段 | 老旧平台或非设备树系统 |
设备树匹配是目前最常用的方式。设备树里写:
uart1: serial@1000 {
compatible = "vendor,uart-v1";
reg = <0x1000 0x100>;
interrupts = <56>;
};
驱动里声明:
static const struct of_device_id uart_of_match[] = {
{ .compatible = "vendor,uart-v1" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uart_of_match);
Platform总线看到compatible字符串一致,就“牵线成功”。
Legacy ID匹配是早期的方式。设备注册时给个名字,驱动也声明个名字,名字对上就行。简单粗暴,但容易冲突。我曾经在一个老项目里看到两个驱动用了同一个名字,结果加载顺序乱了,系统启动时串口死活不工作。排查了半天,最后发现是名字冲突。
避坑指南:我曾经在设备树里写错过compatible字符串,少了个逗号。驱动死活不probe。查了两天才发现是字符串不匹配。所以,设备树里的compatible一定要和驱动里的完全一致,包括大小写和标点。
2.4 为什么需要Platform框架?三个核心价值
你可能会问:搞这么复杂,就为了解耦?
嗯,解耦只是表面。更深层的原因有三个。
- 代码复用:同一份驱动代码,可以跑在不同硬件平台上。只需要更换设备树文件。我在项目中把一套UART驱动从ARM Cortex-A7移植到了RISC-V平台,驱动代码一行没改,只改了设备树。这就是Platform框架的威力。
- 热插拔友好:虽然Platform设备大多是片上外设,不支持物理热插拔。但框架本身支持动态添加/移除设备。比如通过sysfs接口,可以在运行时注册一个新的Platform设备,驱动会自动匹配并probe。这在某些虚拟化场景下很有用。
- 统一驱动模型:Linux内核有统一的设备驱动模型(Device Driver Model)。Platform框架是其中的一部分。它让所有驱动都遵循相同的生命周期管理:probe、remove、suspend、resume。内核可以统一管理电源、休眠、唤醒等操作。没有这个框架,每个驱动都得自己实现一套,那可就乱套了。
一句话总结:Platform框架让驱动开发从“面向硬件”变成了“面向框架”。你只需要关心驱动逻辑,硬件信息交给设备树或ACPI去描述。
2.5 一张图看懂Platform框架
下面这张SVG图,是我自己画的Platform框架核心逻辑。你看一遍就能明白整个流程。
流程很简单:设备注册到总线,驱动注册到总线。总线拿着compatible字符串去匹配。匹配上了,调用驱动的probe。probe里,驱动从设备信息里拿地址、中断号,然后初始化硬件。整个过程,驱动不关心硬件具体在哪,只关心怎么操作。
2.6 小结:Platform框架不是什么黑魔法
说白了,Platform框架就是一层“中间件”。它把硬件描述和驱动逻辑拆开,让两者可以独立演进。
你不需要记住所有细节。只要记住三个核心点:
- 设备:描述“有什么硬件”
- 驱动:描述“怎么操作硬件”
- 总线:负责“谁和谁配对”
我在后续章节里,会带你手写一个完整的Platform驱动。从设备树节点怎么写,到驱动probe里怎么拿资源,一步步来。到时候你就知道,这东西其实没那么神秘。
个人习惯:我每次写新驱动,都会先在设备树里把节点搭好,确认硬件地址和中断号无误。然后再写驱动代码。顺序搞反了,容易出问题。你试试看,这个习惯能帮你省不少调试时间。
公众号:蓝海资料掘金营,微信deep3321