linux固件接口request_firmware

获取固件的正确方法是当需要时从用户空间获取它。一定不要试图从内核空间直接打开包含固件的文件,那是一个易出错的操作, 因为它把策略(以文件名的形式)包含进了内核。正确的方法是使用固件接口:
struct firmware;
request_firmware();
request_firmware_nowait();
release_firmware();

注意:要使用firmware,必须要在配置内核时选上:
Device Drivers —>
Generic Driver Options —>
<*> Userspace firmware loading support
否则会出现: Unknown symbol release_firmware 和: Unknown symbol request_firmware 的错误。

struct firmware

struct firmware {
    size_t size;
    u8 *data;
};

这个结构包含实际的固件,它现在可被下载到设备中。但是请注意:在发送它到硬件之前,必须检查这个文件以确保它是正确的固件映象(设备固件常常包含标识字符串、校验和等等)

request_firmware

#include <linux/firmware.h>
//request_firmware:  send firmware request and wait for it
//@firmware_p: pointer to firmware image
//@name: name of firmware file
//@device: device for which firmware is being loaded
int request_firmware(const struct firmware **fw,
        const char *name,
        struct device *device);

该函数从用户空间得到的数据未作任何检查,所以在编写驱动程序的时候,应对固件映像做数据安全检查,通常检查标志符、校验和等。

request_firmware_nowait

因为request_firmware需要等待用户空间的操作, 返回前将保持休眠,所以不能用于不能睡眠的线程。
request_firmware_nowait是request_firmware的异步请求版本,可以用于不能睡眠的内核线程,但不能用于原子上下文

//@module: module requesting the firmware
//@uevent: sends uevent to copy the firmware image.
//if flag is non-zero els the firmware must be copy manually.
//@name: name of firmware file
//@device: device for which firmware is being loaded
//@context: will be passed over to cont
//@cont: function will be called asynchronously whe firmware request finished
//@fw: may be NULL if firmware request fails
int request_firmware_nowait(
    struct module *module, /*= THIS_MODULE*/
    int uevent,
    const char *name,
    struct device *device,
    gfp_t gfp,
    void *context,/*不由固件子系统使用的私有数据指针*/
    void (*cont)(const struct firmware *fw, void *context));

如果一切正常,request_firmware_nowait 开始固件加载过程并返回0。随后会调用驱动程序的回调函数cont并将保存有firmware的fw作为函数cont的参数。
如果找不到对应位置的文件,过了一段时间后(默认60秒),将用fw=NULL作为参数调用cont。

release_firmware

/*释放firmware结构体以及firmware结构体指向的内存*/
void release_firmware(struct firmware *fw);

/sys中firmware接口

当调用 request_firmware时, 函数将在 /sys/class/firmware 下创建一个以设备名为目录名的新目录,其中包含 3 个属性:
loading :这个属性应当被加载固件的用户空间进程设置为 1。当加载完毕, 它将被设为 0。被设为 -1 时,将中止固件加载。
data :一个用来接收固件数据的二进制属性。在设置 loading 为1后, 用户空间进程将固件写入这个属性。
device :一个链接到 /sys/devices 下相关入口项的符号链接。

创建了 sysfs 入口项之后, 内核将为设备发送uevent事件(“add”)到用户空间,并传递环境变量 $FIRMWARE(固件image名字)和$DEVPATH(固件设备的路径)给处理热插拔的用户空间程序。用户空间管理uevent事件的后台进程udevd接收到事件后,查找udev规则文件并运行规则所定义的动作。规则文件如下:

# /etc/udev/rules.d/50-udev-default.rules
# firmware class requests
SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware.sh"
...

根据规则文件,firmware.sh将会被调用,用户空间的脚本firmware.sh参考如下:

#变量$DEVPATH(固件设备的路径)和$FIRMWARE(固件映像名)应已在环境变量中提供
HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/    #固件映像文件所在目录

echo 1 > /sys/$DEVPATH/loading
cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data
echo 0 > /sys/$DEVPATH/loading

这里有个问题:为什么android系统在开机之后/sys/class/firmware目录下只有timeout一个文件,看不到所创建的设备目录呢?
因为android系统在开机的时候会调用init进程,会调用androidSDK/system/core/init/device.c文件中的handle_firmware_event,handle_firmware_event会查找对应位置的firmware文件是否存在并加载到driver层,如果没有找到,会往loading属性文件写-1,导致fw_load_abort函数被执行,从而导致_request_firmware中一直等待的wait_for_completion(&fw_priv->completion)函数结束,然后fw_destroy_instance被执行,里面会删除在sys/class/firmware目录下创建的,被删除了,当然看不到。
另外还有一种情况,不一定是android系统,当系统起来之后,由于长时间没有往/sys/class/firmware/1-0057/data写入数据,超时导致firmware_class_timeout函数被执行,也会调用fw_load_abort,后面的流程就和上面讲的一样了。

firmware_class.c代码分析

头文件: include/linux/firmware.h
代码: drivers/base/firmware_class.c

你可能感兴趣的:(linux)