linux韌體

metalseed發表於2014-10-10
硬體越來越複雜,硬體的許多功能使用了程式實現,與直接硬體實現相比,韌體擁有處理複雜事物的靈活性和便於升級、維護等優點。韌體(firmware)就是這樣的一段在裝置硬體自身中執行的程式,通過韌體標準驅動程式才能實現特定機器的操作,如:光碟機、燒錄機等都有內部的韌體。

韌體一般存放在裝置上的flash儲存器中,但出於成本和靈活性考慮,許多裝置都將韌體的映像(image)以檔案的形式存放在硬碟中,裝置驅動程式初始化時再裝載到裝置內部的儲存器中。這樣,方便了韌體的升級,並省略了裝置的flash儲存器。

本章分析了驅動程式載入韌體映像檔案的過程。

目錄

[隱藏]

韌體函式介面

Linux核心對裝置韌體的裝載和清除提供了支援介面,可將韌體映像檔案載入到裝置指定的儲存地址,韌體映像檔案的內容由裝置自身來解析,Linux核心只將映像檔案當件未知的二進位制檔案。Linux核心用結構firmware描述韌體映像檔案的內容,該結構列出如下(在include/linux/firmware.h中):

<span class="kw4">struct</span> firmware <span class="br0">{</span>
	size_t size<span class="sy0">;</span>
	<span class="kw4">const</span> u8 <span class="sy0">*</span>data<span class="sy0">;</span>
<span class="br0">}</span><span class="sy0">;</span>


韌體函式介面原型說明

韌體函式介面原型說明如下:

  • 函式request_firmware

函式request_firmware向使用者空間請求提供一個名為name韌體映像檔案並等待完成。引數device為韌體裝載的裝置。檔案內容存入request_firmware 返回,如果韌體請求成功,返回0。該函式從使用者空間得到的資料未做任何檢查,使用者在編寫驅動程式時,應對韌體映像做資料安全檢查,檢查方向由裝置韌體提供商確定,通常有檢查識別符號、校驗和等方法。

int request_firmware(const struct firmware **firmware_p, const char *name, struct device *device)

  • 函式release_firmware

函式release_firmware在完成韌體裝載後,釋放所申請的記憶體塊fw。

void release_firmware(struct firmware *fw);

  • 函式request_firmware_nowait

函式request_firmware_nowait是函式request_firmware的非同步請求版本,用於不能睡眠的核心執行緒中呼叫。引數module為請求韌體的模組;引數uevent為非0時,表示傳送uevent事件用於自動拷貝韌體映像,否則,必須人工拷貝映像;引數name為韌體映像檔案的名字;引數device為裝載韌體的裝置;引數cont為韌體請求完成時呼叫的函式;引數context為函式cont的引數。該函式的原型列出如下:

<span class="kw4">int</span>  request_firmware_nowait<span class="br0">(</span>
	<span class="kw4">struct</span> module <span class="sy0">*</span>module<span class="sy0">,</span> <span class="kw4">int</span> uevent<span class="sy0">,</span>
	<span class="kw4">const</span> <span class="kw4">char</span> <span class="sy0">*</span>name<span class="sy0">,</span> <span class="kw4">struct</span> device <span class="sy0">*</span>device<span class="sy0">,</span> <span class="kw4">void</span> <span class="sy0">*</span>context<span class="sy0">,</span>
	<span class="kw4">void</span> <span class="br0">(</span><span class="sy0">*</span>cont<span class="br0">)</span><span class="br0">(</span><span class="kw4">const</span> <span class="kw4">struct</span> firmware <span class="sy0">*</span>fw<span class="sy0">,</span> <span class="kw4">void</span> <span class="sy0">*</span>context<span class="br0">)</span><span class="br0">)</span>


韌體介面函式的使用方法

當驅動程式需要使用韌體驅動時,在驅動程式的初始化化過程中需要加下如下的程式碼:

<span class="kw1">if</span><span class="br0">(</span>request_firmware<span class="br0">(</span><span class="sy0">&</span>fw_entry<span class="sy0">,</span> $FIRMWARE<span class="sy0">,</span> device<span class="br0">)</span> <span class="sy0">==</span> <span class="nu0">0</span><span class="br0">)</span>  <span class="coMULTI">/*從使用者空間請求映像資料*/</span>
    <span class="coMULTI">/*將韌體映像拷貝到硬體的儲存器,拷貝函式由使用者編寫*/</span>
	copy_fw_to_device<span class="br0">(</span>fw_entry<span class="sy0">-></span>data<span class="sy0">,</span> fw_entry<span class="sy0">-></span>size<span class="br0">)</span><span class="sy0">;</span>   
release<span class="br0">(</span>fw_entry<span class="br0">)</span><span class="sy0">;</span>


使用者還需要在使用者空間提供指令碼通過檔案系統sysfs中的檔案data將韌體映像檔案讀入到核心的緩衝區中。指令碼樣例列出如下:

<span class="co2">#變數$DEVPATH(韌體裝置的路徑)和$FIRMWARE(韌體映像名)應已在環境變數中提供</span>
 
HOTPLUG_FW_DIR<span class="sy0">=/</span>usr<span class="sy0">/</span>lib<span class="sy0">/</span>hotplug<span class="sy0">/</span>firmware<span class="sy0">/</span>    <span class="co2">#韌體映像檔案所在目錄</span>
 
echo <span class="nu0">1</span> <span class="sy0">></span> <span class="sy0">/</span>sys<span class="sy0">/</span>$DEVPATH<span class="sy0">/</span>loading
cat $HOTPLUG_FW_DIR<span class="sy0">/</span>$FIRMWARE <span class="sy0">></span> <span class="sy0">/</span>sysfs<span class="sy0">/</span>$DEVPATH<span class="sy0">/</span>data
echo <span class="nu0">0</span> <span class="sy0">></span> <span class="sy0">/</span>sys<span class="sy0">/</span>$DEVPATH<span class="sy0">/</span>loading


韌體請求函式request_firmware

函式request_firmware請求從使用者空間拷貝韌體映像檔案到核心緩衝區。該函式的工作流程列出如下:

(1)在檔案系統sysfs中建立檔案/sys/class/firmware/xxx/loading和data,"xxx"表示韌體的名字,給檔案loading和data附加讀寫函式,設定檔案屬性,檔案loading表示開/關韌體映像檔案裝載功能;檔案data的寫操作將映像檔案的資料寫入核心緩衝區,讀操作從核心緩衝區讀取資料。

(2)將新增韌體的uevent事件(即"add")通過核心物件模型傳送到使用者空間。

(3)使用者空間管理uevent事件的後臺程式udevd接收到事件後,查詢udev規則檔案,執行規則所定義的動作,與韌體相關的規則列出如下:

<span class="sy0">^-^</span>$ <span class="sy0">/</span>etc<span class="sy0">/</span>udev<span class="sy0">/</span>rules.<span class="me1">d</span><span class="sy0">/</span><span class="nu0">50</span><span class="sy0">-</span>udev<span class="sy0">-</span><span class="kw1">default</span>.<span class="me1">rules</span>
……
<span class="co2"># firmware class requests</span>
SUBSYSTEM<span class="sy0">==</span><span class="st0">"firmware"</span><span class="sy0">,</span> ACTION<span class="sy0">==</span><span class="st0">"add"</span><span class="sy0">,</span> RUN<span class="sy0">+=</span><span class="st0">"firmware.sh"</span>
……

從上述規則可以看出,韌體新增事件將引起執行指令碼firmware.sh。

(4)指令碼firmware.sh開啟"裝載"功能,同命令"cat 映像檔案 > /sys/class/firmware/xxx/data"將映像檔案資料寫入到核心的緩衝區。

(5)映像資料拷貝完成後,函式request_firmware從檔案系統/sysfs登出韌體裝置對應的目錄"xxx"。如果請求成功,函式返回0。

(6)使用者就將核心緩衝區的韌體映像資料拷貝到韌體的記憶體中。然後,呼叫函式release_firmware(fw_entry)釋放給韌體映像分配的緩衝區。

函式request_firmware的呼叫層次圖如圖3所示。它先設定uevent事件為1,然後呼叫裝置驅動程式模型:函式device_register在檔案系統sysfs中建立目錄"xxx",函式kobject_uevent傳送事件,函式device_unregister在裝載完韌體映像資料後清除目錄"xxx"。


圖3 函式request_firmware的呼叫層次圖

函式request_firmware列出如下(在drivers/base/firmware_class.c中):

<span class="kw4">int</span> request_firmware<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">struct</span> firmware <span class="sy0">**</span>firmware_p<span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">char</span> <span class="sy0">*</span>name<span class="sy0">,</span>
                 <span class="kw4">struct</span> device <span class="sy0">*</span>device<span class="br0">)</span>
<span class="br0">{</span>
        <span class="kw4">int</span> uevent <span class="sy0">=</span> <span class="nu0">1</span><span class="sy0">;</span>
        <span class="kw1">return</span> _request_firmware<span class="br0">(</span>firmware_p<span class="sy0">,</span> name<span class="sy0">,</span> device<span class="sy0">,</span> uevent<span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
 
<span class="kw4">static</span> <span class="kw4">int</span> _request_firmware<span class="br0">(</span><span class="kw4">const</span> <span class="kw4">struct</span> firmware <span class="sy0">**</span>firmware_p<span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">char</span> <span class="sy0">*</span>name<span class="sy0">,</span>
		 <span class="kw4">struct</span> device <span class="sy0">*</span>device<span class="sy0">,</span> <span class="kw4">int</span> uevent<span class="br0">)</span>
<span class="br0">{</span>
	<span class="kw4">struct</span> device <span class="sy0">*</span>f_dev<span class="sy0">;</span>
	<span class="kw4">struct</span> firmware_priv <span class="sy0">*</span>fw_priv<span class="sy0">;</span>
	<span class="kw4">struct</span> firmware <span class="sy0">*</span>firmware<span class="sy0">;</span>
	<span class="kw4">struct</span> builtin_fw <span class="sy0">*</span>builtin<span class="sy0">;</span>
	<span class="kw4">int</span> retval<span class="sy0">;</span>
 
	<span class="kw1">if</span> <span class="br0">(</span><span class="sy0">!</span>firmware_p<span class="br0">)</span>
		<span class="kw1">return</span> <span class="sy0">-</span>EINVAL<span class="sy0">;</span>
 
	<span class="sy0">*</span>firmware_p <span class="sy0">=</span> firmware <span class="sy0">=</span> kzalloc<span class="br0">(</span><span class="kw4">sizeof</span><span class="br0">(</span><span class="sy0">*</span>firmware<span class="br0">)</span><span class="sy0">,</span> GFP_KERNEL<span class="br0">)</span><span class="sy0">;</span>
	……  <span class="co1">//省略出錯保護</span>
 
   <span class="coMULTI">/*如果韌體映像在內部__start_builtin_fw指向的地址,拷貝資料到緩衝區*/</span>
	<span class="kw1">for</span> <span class="br0">(</span>builtin <span class="sy0">=</span> __start_builtin_fw<span class="sy0">;</span> builtin <span class="sy0">!=</span> __end_builtin_fw<span class="sy0">;</span>
	     builtin<span class="sy0">++</span><span class="br0">)</span> <span class="br0">{</span>
		<span class="kw1">if</span> <span class="br0">(</span>strcmp<span class="br0">(</span>name<span class="sy0">,</span> builtin<span class="sy0">-></span>name<span class="br0">)</span><span class="br0">)</span>
			<span class="kw1">continue</span><span class="sy0">;</span>
		dev_info<span class="br0">(</span>device<span class="sy0">,</span> <span class="st0">"firmware: using built-in firmware %s<span class="es1">\n</span>"</span><span class="sy0">,</span> name<span class="br0">)</span><span class="sy0">;</span>  <span class="coMULTI">/*列印資訊*/</span>
		firmware<span class="sy0">-></span>size <span class="sy0">=</span> builtin<span class="sy0">-></span>size<span class="sy0">;</span>
		firmware<span class="sy0">-></span>data <span class="sy0">=</span> builtin<span class="sy0">-></span>data<span class="sy0">;</span>
		<span class="kw1">return</span> <span class="nu0">0</span><span class="sy0">;</span>
	<span class="br0">}</span>
	……<span class="co1">//省略列印資訊</span>
    <span class="coMULTI">/*在檔案系統sysfs建立xxx目錄及檔案*/</span>
	retval <span class="sy0">=</span> fw_setup_device<span class="br0">(</span>firmware<span class="sy0">,</span> <span class="sy0">&</span>f_dev<span class="sy0">,</span> name<span class="sy0">,</span> device<span class="sy0">,</span> uevent<span class="br0">)</span><span class="sy0">;</span> 
	<span class="kw1">if</span> <span class="br0">(</span>retval<span class="br0">)</span>
		<span class="kw1">goto</span> error_kfree_fw<span class="sy0">;</span>
 
	fw_priv <span class="sy0">=</span> dev_get_drvdata<span class="br0">(</span>f_dev<span class="br0">)</span><span class="sy0">;</span>
 
	<span class="kw1">if</span> <span class="br0">(</span>uevent<span class="br0">)</span> <span class="br0">{</span>
		<span class="kw1">if</span> <span class="br0">(</span>loading_timeout <span class="sy0">></span> <span class="nu0">0</span><span class="br0">)</span> <span class="br0">{</span>   <span class="coMULTI">/*載入定時器*/</span>
			fw_priv<span class="sy0">-></span>timeout.<span class="me1">expires</span> <span class="sy0">=</span> jiffies <span class="sy0">+</span> loading_timeout <span class="sy0">*</span> HZ<span class="sy0">;</span>
			add_timer<span class="br0">(</span><span class="sy0">&</span>fw_priv<span class="sy0">-></span>timeout<span class="br0">)</span><span class="sy0">;</span>
		<span class="br0">}</span>
 
		kobject_uevent<span class="br0">(</span><span class="sy0">&</span>f_dev<span class="sy0">-></span>kobj<span class="sy0">,</span> KOBJ_ADD<span class="br0">)</span><span class="sy0">;</span>     <span class="coMULTI">/*傳送事件KOBJ_ADD*/</span>
		wait_for_completion<span class="br0">(</span><span class="sy0">&</span>fw_priv<span class="sy0">-></span>completion<span class="br0">)</span><span class="sy0">;</span>
		set_bit<span class="br0">(</span>FW_STATUS_DONE<span class="sy0">,</span> <span class="sy0">&</span>fw_priv<span class="sy0">-></span>status<span class="br0">)</span><span class="sy0">;</span>
		del_timer_sync<span class="br0">(</span><span class="sy0">&</span>fw_priv<span class="sy0">-></span>timeout<span class="br0">)</span><span class="sy0">;</span>
	<span class="br0">}</span> <span class="kw1">else</span>
		wait_for_completion<span class="br0">(</span><span class="sy0">&</span>fw_priv<span class="sy0">-></span>completion<span class="br0">)</span><span class="sy0">;</span>   <span class="coMULTI">/*等待完成韌體映像資料的裝載*/</span>
 
	mutex_lock<span class="br0">(</span><span class="sy0">&</span>fw_lock<span class="br0">)</span><span class="sy0">;</span>
    <span class="coMULTI">/*如果裝載出錯,釋放緩衝區*/</span>
	<span class="kw1">if</span> <span class="br0">(</span><span class="sy0">!</span>fw_priv<span class="sy0">-></span>fw<span class="sy0">-></span>size <span class="sy0">||</span> test_bit<span class="br0">(</span>FW_STATUS_ABORT<span class="sy0">,</span> <span class="sy0">&</span>fw_priv<span class="sy0">-></span>status<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span>
		retval <span class="sy0">=</span> <span class="sy0">-</span>ENOENT<span class="sy0">;</span>
		release_firmware<span class="br0">(</span>fw_priv<span class="sy0">-></span>fw<span class="br0">)</span><span class="sy0">;</span>
		<span class="sy0">*</span>firmware_p <span class="sy0">=</span> NULL<span class="sy0">;</span>
	<span class="br0">}</span>
	fw_priv<span class="sy0">-></span>fw <span class="sy0">=</span> NULL<span class="sy0">;</span>
	mutex_unlock<span class="br0">(</span><span class="sy0">&</span>fw_lock<span class="br0">)</span><span class="sy0">;</span>
	device_unregister<span class="br0">(</span>f_dev<span class="br0">)</span><span class="sy0">;</span>   <span class="coMULTI">/*在檔案系統sysfs登出xxx目錄*/</span>
	<span class="kw1">goto</span> out<span class="sy0">;</span>
 
error_kfree_fw<span class="sy0">:</span>
	kfree<span class="br0">(</span>firmware<span class="br0">)</span><span class="sy0">;</span>
	<span class="sy0">*</span>firmware_p <span class="sy0">=</span> NULL<span class="sy0">;</span>
out<span class="sy0">:</span>
	<span class="kw1">return</span> retval<span class="sy0">;</span>
<span class="br0">}</span>

函式fw_setup_device在檔案系統sysfs中建立韌體裝置的目錄和檔案,其列出如下:

<span class="kw4">static</span> <span class="kw4">int</span> fw_setup_device<span class="br0">(</span><span class="kw4">struct</span> firmware <span class="sy0">*</span>fw<span class="sy0">,</span> <span class="kw4">struct</span> device <span class="sy0">**</span>dev_p<span class="sy0">,</span>
			   <span class="kw4">const</span> <span class="kw4">char</span> <span class="sy0">*</span>fw_name<span class="sy0">,</span> <span class="kw4">struct</span> device <span class="sy0">*</span>device<span class="sy0">,</span>
			   <span class="kw4">int</span> uevent<span class="br0">)</span>
<span class="br0">{</span>
	<span class="kw4">struct</span> device <span class="sy0">*</span>f_dev<span class="sy0">;</span>
	<span class="kw4">struct</span> firmware_priv <span class="sy0">*</span>fw_priv<span class="sy0">;</span>
	<span class="kw4">int</span> retval<span class="sy0">;</span>
 
	<span class="sy0">*</span>dev_p <span class="sy0">=</span> NULL<span class="sy0">;</span>
	retval <span class="sy0">=</span> fw_register_device<span class="br0">(</span><span class="sy0">&</span>f_dev<span class="sy0">,</span> fw_name<span class="sy0">,</span> device<span class="br0">)</span><span class="sy0">;</span>
	<span class="kw1">if</span> <span class="br0">(</span>retval<span class="br0">)</span>
		<span class="kw1">goto</span> out<span class="sy0">;</span>
 
	……
	fw_priv <span class="sy0">=</span> dev_get_drvdata<span class="br0">(</span>f_dev<span class="br0">)</span><span class="sy0">;</span>  <span class="coMULTI">/*從裝置結構中得到私有資料結構*/</span>
 
	fw_priv<span class="sy0">-></span>fw <span class="sy0">=</span> fw<span class="sy0">;</span>
	retval <span class="sy0">=</span> sysfs_create_bin_file<span class="br0">(</span><span class="sy0">&</span>f_dev<span class="sy0">-></span>kobj<span class="sy0">,</span> <span class="sy0">&</span>fw_priv<span class="sy0">-></span>attr_data<span class="br0">)</span><span class="sy0">;</span>  <span class="coMULTI">/*在sysfs中建立可執行檔案*/</span>
	……  <span class="co1">//省略出錯保護</span>
 
	retval <span class="sy0">=</span> device_create_file<span class="br0">(</span>f_dev<span class="sy0">,</span> <span class="sy0">&</span>dev_attr_loading<span class="br0">)</span><span class="sy0">;</span>   <span class="coMULTI">/*在sysfs中建立一般檔案*/</span>
	……  <span class="co1">//省略出錯保護</span>
 
	<span class="kw1">if</span> <span class="br0">(</span>uevent<span class="br0">)</span>
		f_dev<span class="sy0">-></span>uevent_suppress <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span>
	<span class="sy0">*</span>dev_p <span class="sy0">=</span> f_dev<span class="sy0">;</span>
	<span class="kw1">goto</span> out<span class="sy0">;</span>
 
error_unreg<span class="sy0">:</span>
	device_unregister<span class="br0">(</span>f_dev<span class="br0">)</span><span class="sy0">;</span>
out<span class="sy0">:</span>
	<span class="kw1">return</span> retval<span class="sy0">;</span>
<span class="br0">}</span>

函式fw_register_device註冊裝置,在檔案系統sysfs中建立韌體裝置對應的裝置類,存放韌體驅動程式私有資料。其列出如下:

<span class="kw4">static</span> <span class="kw4">int</span> fw_register_device<span class="br0">(</span><span class="kw4">struct</span> device <span class="sy0">**</span>dev_p<span class="sy0">,</span> <span class="kw4">const</span> <span class="kw4">char</span> <span class="sy0">*</span>fw_name<span class="sy0">,</span>
			      <span class="kw4">struct</span> device <span class="sy0">*</span>device<span class="br0">)</span>
<span class="br0">{</span>
	<span class="kw4">int</span> retval<span class="sy0">;</span>
	<span class="kw4">struct</span> firmware_priv <span class="sy0">*</span>fw_priv <span class="sy0">=</span> kzalloc<span class="br0">(</span><span class="kw4">sizeof</span><span class="br0">(</span><span class="sy0">*</span>fw_priv<span class="br0">)</span><span class="sy0">,</span>
						GFP_KERNEL<span class="br0">)</span><span class="sy0">;</span>
	<span class="kw4">struct</span> device <span class="sy0">*</span>f_dev <span class="sy0">=</span> kzalloc<span class="br0">(</span><span class="kw4">sizeof</span><span class="br0">(</span><span class="sy0">*</span>f_dev<span class="br0">)</span><span class="sy0">,</span> GFP_KERNEL<span class="br0">)</span><span class="sy0">;</span>
 
	<span class="sy0">*</span>dev_p <span class="sy0">=</span> NULL<span class="sy0">;</span>
 
	…… <span class="co1">//省略出錯保護</span>
	init_completion<span class="br0">(</span><span class="sy0">&</span>fw_priv<span class="sy0">-></span>completion<span class="br0">)</span><span class="sy0">;</span>    <span class="coMULTI">/*初始化completion機制的等待佇列*/</span>
	fw_priv<span class="sy0">-></span>attr_data <span class="sy0">=</span> firmware_attr_data_tmpl<span class="sy0">;</span>   <span class="coMULTI">/*設定檔案的屬性結構*/</span>
	strlcpy<span class="br0">(</span>fw_priv<span class="sy0">-></span>fw_id<span class="sy0">,</span> fw_name<span class="sy0">,</span> FIRMWARE_NAME_MAX<span class="br0">)</span><span class="sy0">;</span>
 
	fw_priv<span class="sy0">-></span>timeout.<span class="kw2">function</span> <span class="sy0">=</span> firmware_class_timeout<span class="sy0">;</span> <span class="coMULTI">/*超時裝載退出函式*/</span>
	fw_priv<span class="sy0">-></span>timeout.<span class="me1">data</span> <span class="sy0">=</span> <span class="br0">(</span>u_long<span class="br0">)</span> fw_priv<span class="sy0">;</span>
	init_timer<span class="br0">(</span><span class="sy0">&</span>fw_priv<span class="sy0">-></span>timeout<span class="br0">)</span><span class="sy0">;</span>    <span class="coMULTI">/*初始化定時器*/</span>
 
	fw_setup_device_id<span class="br0">(</span>f_dev<span class="sy0">,</span> device<span class="br0">)</span><span class="sy0">;</span>  <span class="coMULTI">/*拷貝device ->bus_id到f_dev中*/</span>
	f_dev<span class="sy0">-></span>parent <span class="sy0">=</span> device<span class="sy0">;</span>    
	f_dev<span class="sy0">-></span>class <span class="sy0">=</span> <span class="sy0">&</span>firmware_class<span class="sy0">;</span>    <span class="coMULTI">/*裝置類例項*/</span>
	dev_set_drvdata<span class="br0">(</span>f_dev<span class="sy0">,</span> fw_priv<span class="br0">)</span><span class="sy0">;</span>   <span class="coMULTI">/*存放裝置驅動的私有資料:f_dev ->driver_data = fw_priv*/</span>
	f_dev<span class="sy0">-></span>uevent_suppress <span class="sy0">=</span> <span class="nu0">1</span><span class="sy0">;</span>
	retval <span class="sy0">=</span> device_register<span class="br0">(</span>f_dev<span class="br0">)</span><span class="sy0">;</span>
	<span class="kw1">if</span> <span class="br0">(</span>retval<span class="br0">)</span> <span class="br0">{</span>
		dev_err<span class="br0">(</span>device<span class="sy0">,</span> <span class="st0">"%s: device_register failed<span class="es1">\n</span>"</span><span class="sy0">,</span> __func__<span class="br0">)</span><span class="sy0">;</span>
		<span class="kw1">goto</span> error_kfree<span class="sy0">;</span>
	<span class="br0">}</span>
	<span class="sy0">*</span>dev_p <span class="sy0">=</span> f_dev<span class="sy0">;</span>
	<span class="kw1">return</span> <span class="nu0">0</span><span class="sy0">;</span>
     ……  <span class="co1">//省略了出錯保護</span>
<span class="br0">}</span>
 
<span class="coMULTI">/*檔案屬性結構例項,設定檔案系統sysfs中data檔案的模式和讀/寫函式*/</span>
<span class="kw4">static</span> <span class="kw4">struct</span> bin_attribute firmware_attr_data_tmpl <span class="sy0">=</span> <span class="br0">{</span>
	.<span class="me1">attr</span> <span class="sy0">=</span> <span class="br0">{</span>.<span class="me1">name</span> <span class="sy0">=</span> <span class="st0">"data"</span><span class="sy0">,</span> .<span class="me1">mode</span> <span class="sy0">=</span> <span class="nu8">0644</span><span class="br0">}</span><span class="sy0">,</span>
	.<span class="me1">size</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">,</span>
	.<span class="me1">read</span> <span class="sy0">=</span> firmware_data_read<span class="sy0">,</span>    <span class="coMULTI">/*從核心緩衝區讀出資料*/</span>
	.<span class="me1">write</span> <span class="sy0">=</span> firmware_data_write<span class="sy0">,</span>   <span class="coMULTI">/*用於將韌體映像檔案的資料寫入到核心緩衝區*/</span>
<span class="br0">}</span><span class="sy0">;</span>
 
<span class="coMULTI">/*裝置類結構例項,含有傳送uevent事件函式和釋放裝置的函式*/</span>
<span class="kw4">static</span> <span class="kw4">struct</span> class firmware_class <span class="sy0">=</span> <span class="br0">{</span>
	.<span class="me1">name</span>		<span class="sy0">=</span> <span class="st0">"firmware"</span><span class="sy0">,</span>      <span class="coMULTI">/*裝置類的名字*/</span>
	.<span class="me1">dev_uevent</span>	<span class="sy0">=</span> firmware_uevent<span class="sy0">,</span> <span class="coMULTI">/*裝置傳送uevent事件的函式*/</span>
	.<span class="me1">dev_release</span>	<span class="sy0">=</span> fw_dev_release<span class="sy0">,</span> <span class="coMULTI">/*釋放裝置的函式*/</span>
<span class="br0">}</span><span class="sy0">;</span>

相關文章