注意:这篇文章上次更新于342天前,文章内容可能已经过时。
本文将解析 Android 系统中固件加载的路径和流程,帮助理解驱动程序如何获取所需的固件文件。
固件的加载流程主要由上图所描述,下面我们结合代码来详细说明。
驱动程序调用
驱动程序在需要加载固件时,会调用 request_firmware() 系列函数,这些函数被定义在 Linux 内核的 include/linux/firmware.h 中,以下是高通 CNSS 平台驱动的示例代码:
1 | int cnss_request_firmware_direct(struct cnss_plat_data *plat_priv, |
内核处理
对于 CNSS 驱动可能使用到的 request_firmware_direct() 或 firmware_request_nowarn() 函数,他们的实现位于内核的 drivers/base/firmware_loader/main.c 文件中。
request_firmware_direct
request_firmware_direct() 函数直接从文件系统加载固件,不会回退到用户空间的 udev 或者 Android 的 init 进程。
1 | /** |
firmware_request_nowarn
而 firmware_request_nowarn() 函数则会尝试从文件系统加载固件,如果失败则会回退到 sysfs fallback 机制,但不会打印警告信息。所谓的 sysfs fallback 机制,就是通过发送 uevent 给用户空间的 init 进程,来查找其他可能的固件路径。
1 | /** |
firmware_fallback_sysfs
无论是 request_firmware_direct() 还是 firmware_request_nowarn(),它们都会调用 _request_firmware() 函数来处理固件加载的具体逻辑。
而在 _request_firmware() 中,我们可以看到这里存在很多 fallback 机制,这里我们重点关注的是 sysfs fallback 机制。
1 | /* called from request_firmware() and request_firmware_work_func() */ |
firmware_fallback_sysfs() 函数的实现定义在 drivers/base/firmware_loader/fallback.c 中,这个文件只有 200 多行代码,其中实现了向用户空间发送 uevent 的逻辑。
主要的函数调用过程如下:
firmware_fallback_sysfs => fw_load_from_user_helper => fw_load_sysfs_fallback
1 | /** |
用户空间处理
firmware 路径的配置文件
在 Android 系统中,固件的加载路径通常由 ueventd.rc 文件配置。这个文件位于 /vendor/etc/ 或 /system/etc/ 目录下。
在 AOSP 代码库中,ueventd.rc 文件的示例可以在以下路径找到:system/core/rootdir/ueventd.rc
其中 firmware_directories 指令用于指定固件的搜索路径。Android 的 init 进程会根据这些路径来查找固件文件。
注意: 中国大陆屏蔽了 Google 的代码托管服务,无法直接访问 cs.android.com 需要通过 VPN 或其他方式访问。
https://cs.android.com/android/platform/superproject/main/+/main:system/core/rootdir/ueventd.rc
1 | import /vendor/etc/ueventd.rc |
ueventd.rc 文件解析
ueventd.rc 文件中定义了多个固件搜索路径,这些路径会被 Android 的 init 进程加载。
在 system/core/init/main.cpp 中,会调用 ueventd_main() 函数来处理 uevent 。
1 | using namespace android::init; |
在 system/core/init/ueventd.cpp 中,ueventd_main() 函数中,会调用静态方法 GetConfiguration() 来读取和解析 ueventd.rc 文件。将解析到的内容保存在 ueventd_configuration 对象中。
然后通过 ueventd_configuration 中的成员变量,也就是 ueventd.rc 文件中定义的内容,来创建各种 uevent 处理器。
其中,就创建了用来处理固件加载的 FirmwareHandler 对象。
1 | int ueventd_main(int argc, char** argv) { |
从 GetConfiguration() 函数中我们可以看到有哪些 ueventd.rc 会被解析。
1 | static UeventdConfiguration GetConfiguration() { |
FirmwareHandler
FirmwareHandler 类是处理固件加载的核心类,它继承自 UeventHandler,并实现了 HandleUevent() 方法来处理接收到的 uevent。
它的构造函数接收固件目录和外部固件处理器的列表,并将这些信息存储在成员变量 firmware_directories_ 中。
1 | class FirmwareHandler : public UeventHandler { |
在 HandleUevent() 方法中,FirmwareHandler 类会检查接收到的 uevent 是否与固件相关,并在需要时创建一个子进程来处理固件加载事件。
在子进程中,它会调用 GetFirmwarePath() 方法来获取固件的完整路径,这个 GetFirmwarePath() 方法实际上是根据 uevent 的信息中拿到固件名称。
然后在 ProcessFirmwareEvent() 方法中,会依次遍历 firmware_directories_ 中的路径,尝试加载固件文件。如果找到了固件文件,就会将其写入到 /sys/<path>/data 文件中,并将 /sys/<path>/loading 文件的内容设置为 0,表示固件加载完成。
如果没有找到固件文件,则会打印错误信息,并将 /sys/<path>/loading 文件的内容设置为 -1,表示加载失败。
1 | void FirmwareHandler::HandleUevent(const Uevent& uevent) { |


