注意:这篇文章上次更新于382天前,文章内容可能已经过时。
This article was last updated382 days ago, the content may be outdated.
本文将解析 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) { |
This article analyzes the firmware loading paths and process in Android systems, helping you understand how drivers obtain the firmware files they need.
The firmware loading flow is mainly described by the diagram above; let’s walk through it in detail with the source code below.
Driver Invocation
When a driver needs to load firmware, it calls functions in the request_firmware() family, which are defined in the Linux kernel’s include/linux/firmware.h. Here is an example from Qualcomm’s CNSS platform driver:
1 | int cnss_request_firmware_direct(struct cnss_plat_data *plat_priv, |
Kernel Handling
The request_firmware_direct() and firmware_request_nowarn() functions that the CNSS driver may use are implemented in the kernel’s drivers/base/firmware_loader/main.c.
request_firmware_direct
request_firmware_direct() loads firmware directly from the filesystem and does not fall back to usermode udev or Android’s init process.
1 | /** |
firmware_request_nowarn
firmware_request_nowarn() also tries to load the firmware from the filesystem first, and falls back to the sysfs fallback mechanism if that fails — but it does not print warning messages. The so-called sysfs fallback mechanism works by sending a uevent to the init process in userspace to search other possible firmware paths.
1 | /** |
firmware_fallback_sysfs
Both request_firmware_direct() and firmware_request_nowarn() call _request_firmware() to handle the actual firmware loading logic.
Inside _request_firmware(), we can see there are many fallback mechanisms; here we focus on the sysfs fallback mechanism.
1 | /* called from request_firmware() and request_firmware_work_func() */ |
The firmware_fallback_sysfs() function is implemented in drivers/base/firmware_loader/fallback.c, a file of only about 200 lines that implements the logic for sending uevents to userspace.
The main call chain is as follows:
firmware_fallback_sysfs => fw_load_from_user_helper => fw_load_sysfs_fallback
1 | /** |
Userspace Handling
The Configuration File for Firmware Paths
On Android systems, the firmware loading paths are usually configured by the ueventd.rc file, located in /vendor/etc/ or /system/etc/.
In the AOSP source tree, an example ueventd.rc can be found at system/core/rootdir/ueventd.rc.
The firmware_directories directive specifies the firmware search paths. Android’s init process searches for firmware files in these directories.
Note: Mainland China blocks Google’s code hosting services, so cs.android.com cannot be accessed directly — you need a VPN or other workarounds.
https://cs.android.com/android/platform/superproject/main/+/main:system/core/rootdir/ueventd.rc
1 | import /vendor/etc/ueventd.rc |
Parsing the ueventd.rc File
The ueventd.rc file defines several firmware search paths, which are loaded by Android’s init process.
In system/core/init/main.cpp, the ueventd_main() function is called to handle uevents.
1 | using namespace android::init; |
In system/core/init/ueventd.cpp, the ueventd_main() function calls the static method GetConfiguration() to read and parse the ueventd.rc file, storing the parsed content in a ueventd_configuration object.
Then, based on the member variables of ueventd_configuration — that is, the content defined in ueventd.rc — various uevent handlers are created.
Among them, a FirmwareHandler object for handling firmware loading is created.
1 | int ueventd_main(int argc, char** argv) { |
From the GetConfiguration() function we can see which ueventd.rc files are parsed.
1 | static UeventdConfiguration GetConfiguration() { |
FirmwareHandler
FirmwareHandler is the core class for handling firmware loading. It inherits from UeventHandler and implements the HandleUevent() method to process received uevents.
Its constructor receives the list of firmware directories and external firmware handlers, and stores them in the member variable firmware_directories_.
1 | class FirmwareHandler : public UeventHandler { |
In the HandleUevent() method, FirmwareHandler checks whether the received uevent is firmware-related, and forks a child process to handle the firmware loading event when needed.
In the child process, it calls GetFirmwarePath() to obtain the full firmware path; this GetFirmwarePath() method actually derives the firmware name from the uevent information.
Then, in ProcessFirmwareEvent(), it iterates over the paths in firmware_directories_ in order, trying to load the firmware file. If a firmware file is found, it writes it to the /sys/<path>/data file and sets the content of /sys/<path>/loading to 0, indicating that the firmware load is complete.
If no firmware file is found, it prints an error message and sets the content of /sys/<path>/loading to -1, indicating that the load failed.
1 | void FirmwareHandler::HandleUevent(const Uevent& uevent) { |


