从函数名上第一次看到这个调用关系时,我第一反应是“这不就是个简单的一对多调用吗”——ACPIBuildProcessRunMethodPhaseCheckSta 和 ACPIBuildProcessDevicePhaseAdr 最后都进了 ACPIGetDevicePresenceAsync。但真正顺着代码走了一遍之后,才发现这个调用关系背后牵扯出一整套ACPI设备构建流程的设计逻辑:阶段划分、异步回调、设备存在性判断、上下文生命周期。这篇就基于我实际梳理代码时的一些分析,聊聊这两个函数为什么要共享同一个异步入口、它们各自的侧重点差异,以及如果要在工程里动这块逻辑,有哪些容易踩的坑。
这组函数名里的 ACPIBuildProcess 前缀,通常对应的不是ACPI规范里那些标准对象(_SB_、_PR_这类),而是操作系统或固件层里为了把ACPI命名空间中的设备节点转换成可被驱动框架识别的内核设备结构而做的一套内部构建流程。RunMethodPhase 是其中负责执行AML控制方法的阶段,DevicePhase 是负责创建和填充设备节点信息的阶段。前者偏动态行为探测,后者偏静态结构组装。两者阶段定位不同,却在某个时刻共同指向同一个设备存在性探测函数,这是这张调用关系图里最有信息量的地方。
1. 从函数命名的层次反推ACPI构建流程的职责边界
1.1 前缀相同不代表职责相同:流程分割后的两类入口
把函数名拆开看,ACPIBuildProcessRunMethodPhaseCheckSta 和 ACPIBuildProcessDevicePhaseAdr 共享 ACPIBuildProcess 这个前缀,说明它们同属一套ACPI构建流程。尾部的 CheckSta 和 Adr 则暴露了各自的工作目标。CheckSta 显然是针对ACPI规范里的 _STA 对象做的一次检查,_STA 的返回值决定了设备在系统当前状态下是否“存在且可用”。而 Adr 对应的则是 _ADR 对象,用来向固件查询设备在所在总线上的地址编码。
如果只是看这两个名字,很容易产生一个误解:ACPIBuildProcessRunMethodPhaseCheckSta 和 ACPIBuildProcessDevicePhaseAdr 是不是同一个流程里的不同命名风格变体?实际上不是。前者是“在运行AML方法的阶段去执行一个叫 CheckSta 的检查动作”,后者是“在设备构建阶段去读取 _ADR 内容”。流程往前推进的节点不一样,拿到结果后各自要做的事情也不同。
在我习惯的工程框架里,ACPI设备构建流程大致会拆成几个连续的Phase:先扫描命名空间、然后针对有方法控制的设备执行必要的AML方法(比如 _INI、_STA),接着把设备基础属性(HID、CID、ADR等)填充进内核设备结构,最后进入驱动绑定。RunMethodPhaseCheckSta 一般放在比较靠前的位置,它回答的是“这个设备在该不该在这个系统实例上暴露”的问题。DevicePhaseAdr 则通常出现在更靠后的阶段,它更像是一次逐设备路径的属性补齐操作,回答的是“这个设备在父总线上怎么寻址”。
1.2 方法执行阶段和设备构建阶段为什么要分开设计
有些读者可能会问:一次ACPI命名空间遍历,挨个设备把 _STA 和 _ADR 都读了不就行了吗,为什么非要拆成两个阶段?
这个问题的答案通常在于设备间的依赖关系。典型场景是某个设备,它的 _STA 执行结果依赖另一个设备是否已经就绪,甚至 _STA 方法自身会触发对 EC(嵌入式控制器)的访问,而EC访问又依赖EC驱动完成初始化。这种情况下必须要有先后顺序,不能一个循环把所有方法全执行完。另一个原因是AML方法执行天然有失败重试和异步等待的需求。_STA 之外的很多方法(_INI、_PS0、_DSM)执行时可能耗时较长,如果把这些动态动作和静态属性装配全部耦合在同一层里,逻辑复杂度会急剧膨胀。利用Phase拆分,把“检查这个设备当前是否存在/是否匹配”和“把这个设备的基础节点信息构建出来”分离开,是ACPI适配层里非常常见的工程手法。
RunMethodPhase 代表的是动作驱动,重点在“方法有没有执行成功、返回状态是什么”。DevicePhase 代表的是结构驱动,重点在“设备对象的字段有没有被正确填充”。一旦理解这一层,再看两个Phase
