天天看點

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"
};
           

繼續閱讀