Scope
Linux 内核提供了一个函数 request_firmware()
来加载固件二进制文件以配置和启用设备。所以在这里我们进一步挖掘它。
Usage of “request_firmware()”
request_firmware()
的声明:
int request_firmware( const struct firmware **firmware_p, const char *name,
struct device *device )
// @firmware_p : pointer to firmware pointer
// @name : name of firmware file
// @device : device for which firmware is loaded
kernel release 里的api:
void release_firmware(struct firmware *firmware)
在内核中调用“request_firmware()”加载固件的示例:
int ret;
ret = request_firmware(&fw, "abc.bin", dev);
if(ret < 0)
pr_err("failed to load firmware\n");
else
update_device_firmware(); // config or enable device with this firmware
Basic work flow of “request_firmware()”
request_firmware()
|
| Y
firmware built in kernel image? --------------- load from kernel image ----------------
| |
| N |
| Y |
load directly from fs ? ----------- directly load from "path"------------ |
| | |
| N | |
| | |
load with file node "/sys/class/firmware/xx.bin/" | |
| | |
| | |
exit exit
Load firmware from kernel image
Linux 内核提供了将固件构建到内核映像中的方法。但是请注意,因为很多固件内容是非 GPL 的,这会弄脏内核。因此,如果没有必要,请不要将固件构建到内核映像中。
启用 CONFIG_FIRMWARE_IN_KERNEL
、CONFIG_EXTRA_FIRMWARE_DIR
和 CONFIG_EXTRA_FIRMWARE
将固件构建到内核映像中:
CONFIG_FIRMWARE_IN_KERNEL=y
CONFIG_EXTRA_FIRMWARE_DIR="firmware" // 这意味着 $(source_dir)/firmware
CONFIG_EXTRA_FIRMWARE="fw_sst_0f28.bin"
如果需要多个固件,请将它们添加为:
CONFIG_EXTRA_FIRMWARE="fw_sst_0f28.bin abc.bin efg.bin"
Mechanism of firmware builtin
当运行命令make bzImage
时,firmware
下的Makefile会生成一个汇编文件fw_sst_0f28.bin.gen.S
:
.section .rodata
_fw_bin:
.incbin "/home/caoxin/linux3.8/firmware/fw_sst_0f28.bin"
.section .rodata.str
_fw_bin_name:
.string "fw_sst_0f28.bin"
.section .builtin_fw
.long _fw_bin_name
.long _fw_bin
.long _fw_end - _fw_bin
该程序集文件包含三个部分:.rodata
、rodata.str
和.builtin_fw
。
.rodata
节将固件数据保存在fw_sst_0f28.bin
中- section
.rodata.str
保存文件名fw_sst_0f28.bin
- section
.builtin_fw
保存固件名称的地址和固件数据的地址
link script vmlinux.lds
涉及以下部分:
.builtin_fw : {
__start_builtin_fw = 。
*(.builtin_fw)
__end_builtin_fw = 。
}
GNU 链接器将所有目标文件中的此部分定位到内核映像的.builtin_fw
部分中。 内核映像在内存中运行后,__start_builtin_fw
和__end_builtin_fw
是本节的开始和结束地址。
当内核调用request_firmware()
时,将检查.builtin_fw
部分中的固件名称是否与传入的固件名称匹配。如果匹配,内核将固件数据复制到缓冲区并且request_firmware()
成功返回。
Load one target firmware from fs(file system) directly
内核使用以下路径中的固件名称搜索固件:
- fw_path_para(kernel command line 定义的
firmware_class.path = xxx
) /lib/firmware/updates UTS_RELEASE
,/lib/firmware/updates
,/lib/firmware UTS_RELEASE
,/lib/firmware
,
注意:
- UTS_RELEASE 是 kernel release 版本号 , 例如
3.8.10-07312013
- 搜索优先级从1 到 5.
如果找到目标固件文件,内核会增加一个缓冲区来保存二进制文件,并且request_firmware()
会成功返回。
Load firmware in userspace
内核在“/sys/class/firmware/”下创建一个目录。 例如,/sys/class/firmware/fw_sst_0f28.bin/{loading, data}
。
在用户空间加载固件的步骤:
1). echo 1 > /sys/class/firmware/fw_sst_0f28.bin/loading
2). cat appropriate_firmware_image > /sys/class/firmware/fw_sst_0f28.bin/data
3). echo 0 > /sys/class/firmware/fw_sst_0f28.bin/loading
当二进制文件进入时,内核会增长一个缓冲区来保存二进制文件,并且request_firmware()
会成功返回。
Reference
- kernel/Documentation/firmware_class/README
- 《Using ld, the GNU linker》 by Jeffrey, Osier