天天看點

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);      

要求使用者空間定位并提供一個固件映像給核心,若成功加載,傳回值是0,并且firmware的内容已經儲存在struct firmware **fw中了;錯誤則傳回錯誤碼,并且fw=NULL。

該函數從使用者空間得到的資料未作任何檢查,是以在編寫驅動程式的時候,應對固件映像做資料安全檢查,通常檢查标志符、校驗和等。

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代碼分析