在linux中,firmware是指固件,是硬件设施自己实行的一段程序,一般存放在设施flash内。在Linux系统中,设施驱动程序处于内核态,而固件文件处于用户态,因此需要一个安全稳定靠谱的机制,用来确保设施驱动程序成功加载固件文件。
linux firmware是什么
固件(firmware)是硬件设施自己实行的一段程序。固件一般存放在设施flash内。而出于本钱和便利性的考虑,一般是先将硬件设施的运行程序打包为一个特定格式的固件文件,存储到终端系统内,通过终端系统给硬件设施进行升级。
Linux内核开发过程中,开发职员调试外设驱动设施,譬如触控,充电,线性马达,存储,WIFI设施等,同样存在需要更新固件的状况。在Linux系统中,设施驱动程序处于内核态,而固件文件处于用户态,因此需要一个安全稳定靠谱的机制,用来确保设施驱动程序成功加载固件文件。
为知道决设施驱动程序从内核态稳定加载用户态固件文件的问题,Linux系统提供了固件子系统。
Linux固件子系统步骤介绍
Linux固件子系统基于sysfs 和uevent机制达成。
驱动程序调用固件系统函数接口申请固件之后,固件子系统用固件编译内核的方法去获得固件;假如获得失败,就用固件缓存的方法去获得固件;假如仍然获得失败,就用默认路径内核直接查找的方法去获得固件。假如还是获得失败,就通过上报uevent消息给init进程。init进程则接收到uevent消息,过滤出subsystem种类为firmware的消息。init进程依据uevent消息内指向的固件信息去查找固件,通过sysfs提供的文件节点接口,把获得的固件内容从用户态写入内核态,从而使驱动程序,获得到固件文件的数据。
Linux固件系统提供了多种在不同场景下获得固件文件的办法。
1)直接编译到内核的方法;
2)固件缓存的方法;
3)直接依据内核指定路径的方法:
4)通过init进程来帮助处置的方法;
Linux固件子系统步骤框图

Linux固件子系统主要函数接口
主要函数接口:
申请固件接口主要种类分为同步和异步。
一般申请固件的过程比较耗时,与处置固件升级的过程比较耗时,因此可以使用异步函数接口达成,或者在驱动程序内先创建工作队列调用同步函数接口达成。
其中:
内核申请固件文件调用 request_firmware函数达成。
内核获得固件文件后调用release_firmware释放有关的内存。

其中:
request_firmware_direct接口只在内核指定的路径内查找固件,不用uevent机制来获得固件。
request_firmware_nowait接口是通过异步的工作队列去获得固件,可以起到不阻塞驱动probe时间有哪些用途。

Linux固件子系统达成过程
request_firmware达成步骤
request_firmware函数通过调用_request_firmware_prepare函数,设置不一样的标志位,达成不一样的差异功能。
_request_firmware_prepare函数:
在打开CONFIG_FW_LOADER宏开关基础上,第一通过调用fw_get_builtin_firmware函数的方法,判断固件文件是不是编译到内核。

接着调用fw_lookup_and_allocate_buf函数,判断全局fw_cache结构内部链接表是不是记录过目前请求firmware的name。假如没有目前请求firmware的name,则动态分配对应的内存空间并且添加目前请求firmware的name到全局的fw_cache结构内的链表。
fw_get_filesystem_firmware函数
主如果通过内核提供的默认路径去查找固件文件,调用kernel_read_file_from_path函数。假如没查找到固件文件,则通过标志位FW_OPT_USERHELPER判断,是不是启用USER_HELPER模式达成。
其中:
Firmware系统内默认路径如下:

默认路径可以通过kernel command line的方法来增加一个路径,通过module_param_string接口传递给变量path来客制化新增路径。

USER_HELPER模式
在内核打开CONFIG_FW_LOADER_USER_HELPER之后,才支持该功能。主要功能就是通过kernel上报uevent消息给到init进程,通过init进程获得固件信息写入底层sysfs节点。
fw_load_from_user_helper函数:
先调用fw_create_instance函数创建device设施,class文件和属性文件,与分配firmware_priv结构体。
接着在 /sys/class/firmware 下将创建一个目录,该目录用设施名作为它的目录名。
该目录包括三个属性:
loading:
设置为 1:该属性由负责装载固件的用户空间设置1开始;
设置为 0:当装载过程完毕;
设置为 -1:将终止固件装载过程。
data:
用来接收固件数据,在设置完 loading 后,用户空间进程把固件写入该属性。
device:
/sys/devices 下相应入口的符号链接。
timeout:
默认申请firmware通过uevent方法最大超时时间为60S,支持上层写入超时时间。
_request_firmware_load函数:
第一先禁用uevent上报,通过调用device_add函数添加设施,触发调用firmware_uevent函数。其中,填充uevent上报的信息格式,包含固件的名字,超时时间,是不是异步。

下一步则启用uevent上报功能,同时调用kobject_uevent函数,上报add动作种类给到上层ueventd。

接着调用fw_state_wait_timeout函数,在预设的超时时间内等待上层ueventd的处置。
若超时时间达到或者收到完成量唤醒,则释放之前申请的内存,释放device,class等内存信息。
ueventd有关firmware处置步骤
Ueventd是init进程内要紧的模块,它主要处置selinux,dev设施创建,监听kernel上报uevent消息,firmware固件加载等内容。
FirmwareHandler处置步骤:
FirmwareHandler内的HandleUevent办法主如果处置firmware固件加载和底层节点的交互步骤。
第一先判断uevent消息的subsystem种类是firmware字段才进行处置,这个种类只有kernel内firmware模块才会上报。
HandleUevent主如果通过一个主线程创建不一样的子线程,并行分别处置来自kernel的不同驱动的firmware请求。

ProcessFirmwareEvent函数
第一是循环判断ueventd支持的路径内检索固件文件是不是存在;若存在,则写入底层loading属性文件为1,同时拷贝获得的固件文件,写入到底层data文件。完成之后则写入底层loading属性文件为0。
至此,kernel就获得到了用户空间写入的固件文件信息。

其中:
ueventd 默认支持搜索固件的路径:
来自 ueventd.rc文件内指定的firmware_directory。






