天天看点

Linux固件加载过程简析

Linux内核模块使用固件程序时,正确的做法是使用request_firmware_nowait()一类固件接口来获取固件数据。

在使用firmware前,必须对内核做如下配置。

Device Drivers —> 
Generic Driver Options —> 
<*> Userspace firmware loading support 
           

否则会出现: Unknown symbol release_firmware 和: Unknown symbol request_firmware 的错误。

下面是request_firmware_nowait函数的原型

int
request_firmware_nowait(
	struct module *module, bool 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。

其中struct firmware定义如下:

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

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

跟踪request_firmware_nowait的实现过程:

request_firmware_nowait
    _request_firmware_nowait
        request_firmware_work_func
            _request_firmware(desc);
        desc->cont(fw, desc->context);//调用传入的回调函数
           
static int _request_firmware(struct fw_desc *desc)
{
    // 为固件分配内存,拷贝固件名到firmware->priv->fw_id
    ret = _request_firmware_prepare(&fw, desc);
    // 返回骨架加载超时时间,默认是60s
    timeout = firmware_loading_timeout();
    
    timeout = usermodehelper_read_lock_wait(timeout);
    // 从文件系统读固件数据
    ret = fw_get_filesystem_firmware(desc->device, fw->priv,
					 desc->dest_addr, desc->dest_size);
					 
	usermodehelper_read_unlock();
	
	if (ret < 0) {
		release_firmware(fw);
		fw = NULL;
	}
	
	*desc->firmware_p = fw;
}
           

我们首先关注fw_get_filesystem_firmware函数,这个函数从文件系统指定路径读取固件数据。

static int fw_get_filesystem_firmware(struct device *device,
				      struct firmware_buf *buf,
				      phys_addr_t dest_addr, size_t dest_size)
{
    for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
        struct file *file;
        
        /* skip the unset customized path */
		if (!fw_path[i][0])
			continue;
		// 获得固件在文件系统的路径
		snprintf(path, PATH_MAX, "%s/%s", fw_path[i], buf->fw_id);
		// open文件
		file = filp_open(path, O_RDONLY, 0);
		rc = fw_read_file_contents(file, buf);
		fput(file);
    }
}
           

fw_get_filesystem_firmware从文件系统文件中读取到固件信息保存到内核的firmware_buf变量中,最后将数据传递给最开始的回调函数,给对应的模块使用。

fw_path是内核模块中指定的固件位置,我们可以增加自己的文件目录到该数组中,使内核可以找到更多目录下的固件。

/* direct firmware loading support */
static char fw_path_para[256];
static const char * const fw_path[] = {
	fw_path_para,
	"/lib/firmware/updates/" UTS_RELEASE,
	"/lib/firmware/updates",
	"/lib/firmware/" UTS_RELEASE,
	"/lib/firmware",
	"/firmware/image"
};
           

继续阅读