国产毛片a精品毛-国产毛片黄片-国产毛片久久国产-国产毛片久久精品-青娱乐极品在线-青娱乐精品

查看: 3083|回復(fù): 0
打印 上一主題 下一主題

Linux 設(shè)備驅(qū)動的固件加載詳解

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2019-1-23 15:27:06 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
關(guān)鍵詞: Linux
作為一個驅(qū)動作者, 你可能發(fā)現(xiàn)你面對一個設(shè)備必須在它能支持工作前下載固件到它里面. 硬件市場的許多地方的競爭是如此得強烈, 以至于甚至一點用作設(shè)備控制固件的 EEPROM 的成本制造商都不愿意花費. 因此固件發(fā)布在隨硬件一起的一張 CD 上, 并且操作系統(tǒng)負(fù)責(zé)傳送固件到設(shè)備自身.

硬件越來越復(fù)雜,硬件的許多功能使用了程序?qū)崿F(xiàn),與直接硬件實現(xiàn)相比,固件擁有處理復(fù)雜事物的靈活性和便于升級、維護等優(yōu)點。固件(firmware)就是這樣的一段在設(shè)備硬件自身中執(zhí)行的程序,通過固件標(biāo)準(zhǔn)驅(qū)動程序才能實現(xiàn)特定機器的操作,如:光驅(qū)、刻錄機等都有內(nèi)部的固件。

固件一般存放在設(shè)備上的flash存儲器中,但出于成本和靈活性考慮,許多設(shè)備都將固件的映像(image)以文件的形式存放在硬盤中,設(shè)備驅(qū)動程序初始化時再裝載到設(shè)備內(nèi)部的存儲器中。這樣,方便了固件的升級,并省略了設(shè)備的flash存儲器。

一、驅(qū)動和固件的區(qū)別

計算機領(lǐng)域來說,驅(qū)動和固件從來沒有過明確的定義,就好像今天我們說內(nèi)存,大部分人用來表示SDRAM,但也有人把Android里的“固化的Flash/Storage"稱為“內(nèi)存”,你不能說這樣說就錯了,因為這確實是一種“內(nèi)部存儲”。

但在Linux Kernel中,Driver和Firmware是有明確含義的,

1、驅(qū)動
Driver是控制被操作系統(tǒng)管理的外部設(shè)備(Device)的代碼段。很多時候Driver會被實現(xiàn)為LKM,但這不是必要條件。driver通過driver_register()注冊到總線(bus_type)上,代表系統(tǒng)具備了驅(qū)動某種設(shè)備(device)的能力。當(dāng)某個device被注冊到同樣的總線的時候(通常是總線枚舉的時候發(fā)現(xiàn)了這個設(shè)備),總線驅(qū)動會對driver和device會通過一定的策略進行binding(即進行匹配),如果Binding成功,總線驅(qū)動會調(diào)用driver的probe()函數(shù),把設(shè)備的信息(例如端口,中斷號等)傳遞給驅(qū)動,驅(qū)動就可以對真實的物理部件進行初始化,并把對該設(shè)備的控制接口注冊到Linux的其他子系統(tǒng)上(例如字符設(shè)備,v4l2子系統(tǒng)等)。這樣操作系統(tǒng)的其他部分就可以通過這些通用的接口來訪問設(shè)備了。

2、固件

Firmware,是表示運行在非“控制處理器”(指不直接運行操作系統(tǒng)的處理器,例如外設(shè)中的處理器,或者被用于bare metal的主處理器的其中一些核)中的程序。這些程序很多時候使用和操作系統(tǒng)所運行的處理器完全不同的指令集。這些程序以二進制形式存在于Linux內(nèi)核的源代碼樹中,生成目標(biāo)系統(tǒng)的時候,通常拷貝在/lib/firmware目錄下。當(dāng)driver對device進行初始化的時候,通過request_firmware()等接口,在一個用戶態(tài)helper程序的幫助下,可以把指定的firmware加載到內(nèi)存中,由驅(qū)動傳輸?shù)街付ǖ脑O(shè)備上。

所以,總的來說,其實driver和firmware沒有什么直接的關(guān)系,但firmware通常由驅(qū)動去加載。我們討論的那個OS,一般不需要理解firmware是什么,只是把它當(dāng)做數(shù)據(jù)。firmware是什么,只有使用這些數(shù)據(jù)的那個設(shè)備才知道。好比你用一個電話,電話中有一個軟件,這個軟件你完全不關(guān)心如何工作的,你換這個軟件的時候,就可以叫這個軟件是“固件”,但如果你用了一個智能手機,你要細(xì)細(xì)關(guān)系什么是上面的應(yīng)用程序,Android平臺,插件之類的細(xì)節(jié)內(nèi)容,你可能就不叫這個東西叫“固件”了。

如何解決固件問題呢?你可能想解決固件問題使用這樣的一個聲明:
static char my_firmware[] = { 0x34, 0x78, 0xa4, ... };

但是, 這個方法幾乎肯定是一個錯誤. 將固件編碼到一個驅(qū)動擴大了驅(qū)動的代碼, 使固件升級困難, 并且非常可能產(chǎn)生許可問題. 供應(yīng)商不可能已經(jīng)發(fā)布固件映象在 GPL 之下, 因此和 GPL-許可的代碼混合常常是一個錯誤. 為此, 包含內(nèi)嵌固件的驅(qū)動不可能被接受到主流內(nèi)核或者被 Linux 發(fā)布者包含.

二、內(nèi)核固件接口
        正確的方法是當(dāng)你需要它時從用戶空間獲取它. 但是, 請抵制試圖從內(nèi)核空間直接打開包含固件的文件的誘惑; 那是一個易出錯的操作, 并且它安放了策略(以一個文件名的形式)到內(nèi)核. 相反, 正確的方法時使用固件接口, 它就是為此而創(chuàng)建的:
[cpp] view plain copy
1. #include   
2.   
3. int request_firmware(const struct firmware **fw, char *name, struct device *device);  

函數(shù)request_firmware向用戶空間請求提供一個名為name固件映像文件并等待完成。參數(shù)device為固件裝載的設(shè)備。文件內(nèi)容存入request_firmware 返回,如果固件請求成功,返回0。該函數(shù)從用戶空間得到的數(shù)據(jù)未做任何檢查,用戶在編寫驅(qū)動程序時,應(yīng)對固件映像做數(shù)據(jù)安全檢查,檢查方向由設(shè)備固件提供商確定,通常有檢查標(biāo)識符、校驗和等方法。

調(diào)用 request_firmware 要求用戶空間定位并提供一個固件映象給內(nèi)核; 我們一會兒看它如何工作的細(xì)節(jié). name 應(yīng)當(dāng)標(biāo)識需要的固件; 正常的用法是供應(yīng)者提供的固件文件名. 某些象 my_firmware.bin 的名子是典型的. 如果固件被成功加載, 返回值是 0(負(fù)責(zé)常用的錯誤碼被返回), 并且 fw 參數(shù)指向一個這些結(jié)構(gòu):
[cpp] view plain copy
1. struct firmware {  
2.  size_t size;  
3.  u8 *data;   
4. };  

那個結(jié)構(gòu)包含實際的固件, 它現(xiàn)在可被下載到設(shè)備中. 小心這個固件是來自用戶空間的未被檢查的數(shù)據(jù); 你應(yīng)當(dāng)在發(fā)送它到硬件之前運用任何并且所有的你能夠想到的檢查來說服你自己它是正確的固件映象. 設(shè)備固件常常包含標(biāo)識串, 校驗和, 等等; 在信任數(shù)據(jù)前全部檢查它們.

在你已經(jīng)發(fā)送固件到設(shè)備前, 你應(yīng)當(dāng)釋放 in-kernel 結(jié)構(gòu), 使用:
[cpp] view plain copy
1. void release_firmware(struct firmware *fw);  
因為 request_firmware 請求用戶空間來幫忙, 它保證在返回前睡眠. 如果你的驅(qū)動當(dāng)它必須請求固件時不在睡眠的位置, 異步的替代方法可能要使用:
[cpp] view plain copy
1. int request_firmware_nowait(struct module *module,  
2.        char *name, struct device *device, void *context,  
3.              void (*cont)(const struct firmware *fw, void *context));  

這里額外的參數(shù)是 moudle( 它將一直是 THIS_MODULE), context (一個固件子系統(tǒng)不使用的私有數(shù)據(jù)指針), 和 cont. 如果都進行順利, request_firmware_nowait 開始固件加載過程并且返回 0. 在將來某個時間, cont 將用加載的結(jié)果被調(diào)用. 如果由于某些原因固件加載失敗, fw 是 NULL.

三、固件如何工作

固件子系統(tǒng)使用 sysfs 和熱插拔機制. 當(dāng)調(diào)用 request_firmware, 一個新目錄在 /sys/class/firmware 下使用你的驅(qū)動的名子被創(chuàng)建. 那個目錄包含 3 個屬性:

loading
這個屬性應(yīng)當(dāng)被加載固件的用戶空間進程設(shè)置為 1. 當(dāng)加載進程完成, 它應(yīng)當(dāng)設(shè)為 0. 寫一個值 -1 到 loading 會中止固件加載進程.

data

data 是一個二進制的接收固件數(shù)據(jù)自身的屬性. 在設(shè)置 loading 后, 用戶空間進程應(yīng)當(dāng)寫固件到這個屬性.

device

這個屬性是一個符號連接到 /sys/devices 下面的被關(guān)聯(lián)入口項.

一旦創(chuàng)建了 sysfs 入口項, 內(nèi)核為你的設(shè)備產(chǎn)生一個熱插拔事件. 傳遞給熱插拔處理者的環(huán)境包括一個變量 FIRMWARE, 它被設(shè)置為提供給 request_firmware 的名子. 這個處理者應(yīng)當(dāng)定位固件文件, 并且拷貝它到內(nèi)核使用提供的屬性. 如果這個文件無法找到, 處理者應(yīng)當(dāng)設(shè)置 loading 屬性為 -1.

如果一個固件請求在 10 秒內(nèi)沒有被服務(wù), 內(nèi)核就放棄并返回一個失敗狀態(tài)給驅(qū)動. 超時周期可通過 sysfs 屬性 /sys/class/firmware/timeout 屬性改變.

使用 request_firmware 接口允許你隨你的驅(qū)動發(fā)布設(shè)備固件. 當(dāng)正確地集成到熱插拔機制, 固件加載子系統(tǒng)允許設(shè)備簡化工作"在盒子之外" 顯然這是處理問題的最好方法.

但是, 請允許我們提出多一條警告: 設(shè)備固件沒有制造商的許可不應(yīng)當(dāng)發(fā)布. 許多制造商會同意在合理的條款下許可它們的固件, 如果客氣地請求; 一些其他的可能不何在. 無論如何, 在沒有許可時拷貝和發(fā)布它們的固件是對版權(quán)法的破壞并且招致麻煩.

四、固件接口函數(shù)的使用方法
       當(dāng)驅(qū)動程序需要使用固件驅(qū)動時,在驅(qū)動程序的初始化化過程中需要加下如下的代碼:
[cpp] view plain copy
1. if(request_firmware(&fw_entry, $FIRMWARE, device) == 0)  /*從用戶空間請求映像數(shù)據(jù)*/  
2.   
3. /*將固件映像拷貝到硬件的存儲器,拷貝函數(shù)由用戶編寫*/  
4. copy_fw_to_device(fw_entry->data, fw_entry->size);     
5. release(fw_entry);  
    用戶還需要在用戶空間提供腳本通過文件系統(tǒng)sysfs中的文件data將固件映像文件讀入到內(nèi)核的緩沖區(qū)中。腳本樣例列出如下:
[cpp] view plain copy
1. #變量$DEVPATH(固件設(shè)備的路徑)和$FIRMWARE(固件映像名)應(yīng)已在環(huán)境變量中提供  
2.   
3. HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/    #固件映像文件所在目錄  
4.   
5. echo 1 > /sys/$DEVPATH/loading  
6. cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data  
7. echo 0 > /sys/$DEVPATH/loading  

五、固件請求函數(shù)request_firmware

函數(shù)request_firmware請求從用戶空間拷貝固件映像文件到內(nèi)核緩沖區(qū)。該函數(shù)的工作流程列出如下:

a -- 在文件系統(tǒng)sysfs中創(chuàng)建文件/sys/class/firmware/xxx/loading和data,"xxx"表示固件的名字,給文件loading和data附加讀寫函數(shù),設(shè)置文件屬性,文件loading表示開/關(guān)固件映像文件裝載功能;文件data的寫操作將映像文件的數(shù)據(jù)寫入內(nèi)核緩沖區(qū),讀操作從內(nèi)核緩沖區(qū)讀取數(shù)據(jù)。

b -- 將添加固件的uevent事件(即"add")通過內(nèi)核對象模型發(fā)送到用戶空間。

c -- 用戶空間管理uevent事件的后臺進程udevd接收到事件后,查找udev規(guī)則文件,運行規(guī)則所定義的動作,與固件相關(guān)的規(guī)則列出如下:
[cpp] view plain copy
1. $ /etc/udev/rules.d/50-udev-default.rules  
2. ……  
3. # firmware class requests  
4. SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware.sh"  
5. ……  

從上述規(guī)則可以看出,固件添加事件將引起運行腳本firmware.sh。

d -- 腳本firmware.sh打開"裝載"功能,同命令"cat 映像文件 > /sys/class/firmware/xxx/data"將映像文件數(shù)據(jù)寫入到內(nèi)核的緩沖區(qū)。

e -- 映像數(shù)據(jù)拷貝完成后,函數(shù)request_firmware從文件系統(tǒng)/sysfs注銷固件設(shè)備對應(yīng)的目錄"xxx"。如果請求成功,函數(shù)返回0。

f -- 用戶就將內(nèi)核緩沖區(qū)的固件映像數(shù)據(jù)拷貝到固件的內(nèi)存中。然后,調(diào)用函數(shù)release_firmware(fw_entry)釋放給固件映像分配的緩沖區(qū)。

以下課程可免費試聽C語言電子PCBSTM32、Linux、FPGA、Python、安卓等。
想學(xué)習(xí)的你和我聯(lián)系預(yù)約就可以免費聽課了。宋工Q35--24-65--90-88   Tel/WX:173--17--95--19--08


您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

關(guān)于我們  -  服務(wù)條款  -  使用指南  -  站點地圖  -  友情鏈接  -  聯(lián)系我們
電子工程網(wǎng) © 版權(quán)所有   京ICP備16069177號 | 京公網(wǎng)安備11010502021702
快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 久久99久久99精品免费看动漫 | 欧美不卡一区 | 精品精品国产高清a毛片 | 黄色片网站免费观看 | 99久久www免费人成精品 | 日韩免费高清一级毛片 | 青青久在线视频免费观看 | 日日视频 | 久久精品国产99久久99久久久 | 九九热九九热 | 无人视频在线观看完整版免费下载 | 一级黄色a视频 | 亚洲天堂一区二区三区四区 | 一级毛片 在线播放 | 狼狼色丁香久久女婷婷综合 | 69视频在线观看免费 | 欧美高清日本三级人妇 | 在线视频免费观看a毛片 | 色在线影院 | 国产色婷婷精品综合在线观看 | 亚洲黄色免费在线观看 | 亚洲欧美国产精品专区久久 | 欧美在线天堂 | 国产精品动漫视频网站 | 在线观看精品国产入口 | 天堂在线最新版在线www | 韩国一级毛片a级免观看 | 国产成人99久久亚洲综合精品 | 婷婷在线播放 | 伊人青青久久 | 黄视频网址| 99视频在线永久免费观看 | 欧美日韩高清在线观看一区二区 | 日本高清视频在线 | 日本免费一区视频 | 免看一级a毛片一片成人不卡 | 91久热| 四虎影视免费永久在线观看 | 四虎永久精品免费观看 | 手机看片国产永久1204 | 国产福利在线视频 |