標籤:
第八章 讓開發板發出聲音:蜂鳴器驅動
linux驅動的代碼重用有很多種方法。可以採用標準C 程式的方式。將要重用的代碼放在其他的檔案(在標頭檔中聲明〉中。如果要使用某些功能, include 相應的標頭檔即可〈這.方式稱為靜態重用〉。也可以使用另外一種動態重用的方式,也就是一個Linux 驅動可以使用另外一個Linux驅動中的資源(函數、變數、宏等)。如果Linux 驅動的代碼不多,將所有的代碼都放到一個檔案中並沒有什麼不妥,但對於複雜的Linux 驅動,就需要使用多個原始碼檔案存放不同的功能代碼,這樣做有利於代碼分類和管理。當然,如果Linux 驅動要使用第三方的原始碼, 那麼就不得不編譯多個原始碼檔案,最終便成.ko 檔案或編譯進Linux核心。C 或C++語言中編譯多個原始碼檔案時,如果a.c 使用了 b.c 檔案中的函數,需要在a.c 檔案中使用 extern 預先定義b.c中的函數, extern 的作用就是告訴編譯器該函數的函數名、參數個數、參數類型和傳回值類型。這些資訊對於將a.c 編譯成a.o已經足夠了。等到將a.o 和b.o 連結成可執行檔或程式庫時,編譯器再到b.c中尋找函數的具體要現。也就是說, extern 只在編譯階段起作用。除此之外,還可以使用b.h 檔案定義b.c 中的函數,然後在a.c中包含b.h 檔案。對於有些C/C++編譯器,可以省略extern 關鍵字。不過為了更通用,建議還是加上extern。
代碼重用分為靜態和動態兩種。另外一種代碼共用的方式:模組依賴,也稱為匯出符號。如果只能用一句話解釋如何利用匯出符號實現代碼共用,這句話就是“在一個驅動模組裡使用另一個驅動模組裡的被匯出的符號(常量、變數、函數等)”。1.由於Linux 驅動模組的初始化函數進行了某些操作而崩潰,從而導致初始化函數無法正常返回。這種情況的表現是當前Linux 驅動模組沒被任何其他的Linux驅動模組使用,但卻顯示已經被引用了一次了一次。2.在使用rmmod 命令卸載Linux 驅動時,系統會調用卸載函數,只有卸載函數成功返回肘, Linux 驅動才會被翻載。如果卸載函數被阻塞(可能是死迴圈、並發等情況引起的阻塞), rmmod 命令也會被阻塞. 也就是說永遠不會執行到卸載Linux 驅動模組的代碼。這種情況的表現是一執行rmmod 命令就會停在那不動了,永遠也不會返回到系統的操作提示符。
首先看第1 種情況。這種情況的關鍵是引用計數器的值和引用者不一致。實際上引用者是不存在的,因此,只需要將當前的Linux 驅動模組的引用計數器清零即可。修改引用計數器可以使用下面兩個函數。
// 使module 指向的Linu x 驅動模煥的引用計數器加l ,成功返回1 ,失敗返回0。
static inl 立ne int try_m。dule_get(struct module *module)
// 使module 指向的Linux 驅動模組的引用計數器減1。
extern void modul e_put (struct module *module);
第2 種情況的問題根源就是卸載函數,因此,只要將原來的卸載函數替換成一個空的卸載函數即可。
第九章 硬體抽象層:HAL
HAL ( Hardware Abstraction Layer,硬體抽象腔,〉是建立在Linux 驅動之上的一套翻字型檔。這套程式庫並不屬於Linux 核心,而是屬於Linux 核心層之上的應用程式層。Google 為Android 增加HAL 的主要目
的除了盡量避免應用程式直接存取Linux 驅動外,還有一個重要原因,那就是保護“私人財產”。對於那些既想發布基於Android的Linux 驅動程式,又不想將核心商務邏輯公開的企業或個人,簡直就是福音。
在傳統的Linux系統中Linux驅動一般有兩種類型的代碼:訪問硬體寄存器的代碼和商務邏輯代碼。對於訪問硬體寄存器的代碼,並沒有什麼秘密可言,因為這都是調用的Linux 核心的標準函數(ioread32 、iowrite32 等)進行的標準操作。而Linux 驅動的商務邏輯部分對擊有些企業或個人並不想將原始碼公開。儘管這些Linux 驅動都是免費給使用者使用的,但由於這些Linux 驅動的實現涉及一些技術專利或商業秘密,如果公開原始碼會有很大麻煩。但作為Linux.驅動,又不得不公開原始碼。這是由於Linux 核心採用了GPL 協議,而GPL 協議要求所有使用基於GPL 協議的原始碼的程式必須開源(由於Linux 驅動屬於Linux 核心的一部分,因此Linux 驅動必須開源)。
總而言之,Google 為Android加入HAL主要有如下的目的:
1.統一硬體的調用介面。由於HAL 有標準的調用接臥,所以可以利用HAL屏蔽Linux 驅動複雜、不統一的介面。
2.解決了GPL著作權問題。由於Linux 核心基於GPL協議,而Android 基於Apache Licence 2.0 協議。因此Google 玩了個“穿越”將原本位於Linux驅動中的敏感代碼向上移了一個層次。這樣這些敏感代碼就擺脫了GPL 協議的束縛 。那些不想開源的Linux驅動作者也就沒必要開源了。
3.針對一些特殊的要求。對於有些硬體,可能需要訪問一些使用者空間的資源,或在核心空間不方便完成的工作以及特殊需求。在這種情況下,可以利用位於使用者空間的HAL 代碼來輔助Linux驅動完成一些工作。
Google 為了滿足這些不想開源的Linux 驅動作者的要求,在Android階層中的系統運行庫層增加了一個HAL。HAL 並不是Linux 核心的一部分,而是位於Android的系統運行庫層,而Android採用了Apache Licence2.0協議發布,Apache Licence2.0協議並未要求使用基於Apache Licence2.0協議的原始碼的軟體也必須開源。由於HAL屬於Android的一部分,自然也不必開源了。
接下來是應用LED驅動的移植來示範支援HAL的linux驅動程式,讓我們初步瞭解其步驟。
*編寫調用HAL 模缺的Service
1.由於JNI 會用到很多C++代碼,因此, LedHalService.cpp 不能改成LedHalService.c。
2.LedHalService.cpp 檔案中引用了leds_hat.h標頭檔。該檔案屬於HAL 模組的原始碼檔案,己在Android.mk 檔案中指leds__hat.h 檔案所在的路徑( hardware/leds_hal),在Android.mk 中指定的都屬於系統路徑,因此在include 該檔案時要用角括弧(<...〉)。
3.JNI 方法在預設情況下有一定的命名規則(Java_packagename_classname_methodname),但可以通過RegisterNatives 自訂則JNI方法。該方法的最後一個參數需要指定要註冊的JNI函數的個數。
4.利用slieof(methods)/sizeof(methods[0]計算methods數組長度,由於數組的每一個元素佔用的位元組個數是相等的
(JNINativeMethod類型,由3個指標組成,32位系統和64位系統每個數組元素占的位元組數分別為12和24)。因此用整個數組佔用的
位元組數除以每一個數組元素佔用的位元組數(一定會被整除的)就是數組的長度。
5.methods數組的元素類型是JNINativeMethod。
注意:1.HAL 模組庫檔案的存放路徑有兩個: /system/lib/hw 和/vendor/lib/hw 。hw_get_ module 函數會先從/system/lib/hw 目錄根據庫檔案命名規則尋找庫檔案。如果/system/lib/hw 目錄中未找到庫檔案, hw_get_ module 會按同樣的規則在/vendor/lib/hw 目錄中尋找。
2.HAL 模組庫檔案的命名規則是ID.suffix.so。其中TD 通過hw_get_ module 函數的id 參數指定。suffix (尾碼)通過屬性檔案指定。
3.hw _get_module 會在Android 系統的屬性檔案中根據variant_keys 數組中定義的4 個key依次尋找suffix 。如果未找到suffix ,使用預設的suffix (default )。
Android 系統的屬性檔案共有如下4個:
/default.prop ;
/system/build.prop;
/system/default.prop;
/data/local.prop .
Android 在啟動時會自動裝載這些屬性檔案。如果在多個屬性檔案中都定義了同一個Key 和Value,那麼只用第一個Key 被擷取。例如,在/default.prop 檔案中定義了ro.product.board 的值為abc,而在/system/build.prop 檔案中定義了ro.product.board 的值為xyz。那麼hw_get_ module 函數會把/default.prop 檔案中的abc 作為HAL 模組庫檔案的尾碼,而不會再讀取/system/build.prop 檔案中的xyz。
在Android系統中使用Linux 驅動有兩種方式。一種就是通過傳統的方式直接與Linux 驅動互動。例如,直接讀寫裝置檔案的資料。另外一種是Android特有的,就是通過HAL 模組 。HAL 模組本質上就是通過Linux 共用庫(.so)與Linux驅動互動,然後應用程式再訪問Linux共用庫。早期的HAL模組由應用程式直接按訪問Linux共字型檔的方式調用。而高版本的Android 系統為HAL增加了Stub 。換句話說,就是為每一個HAL共用庫指定一個ID, 再利用這個ID 配合一定的規則找到Linux 共字型檔,這樣Linux 共字型檔更換檔案名稱,移動位置都很方便(因為HAL 共用庫的路徑和檔案名稱都不是定死的),Google也建議使用Stub 的方式編寫HAL模組。
第十章 嵌入式linux的調用技術
printk 函數在控制台(也稱為終端)顯示訊息是通過/dev/console 裝置檔案實現的,該裝置檔案只在字元介面的控制台下才起作用p 所以printk 函數只有用在字元介面的控制台上才能正常輸出訊息。printk 函數在前面的章節己多次使用過。該函數的用法與printf 函數類似,只不過printk函數運行在核心空間, printf函數運自行在使用者空間。也就是說,像Linux 驅動這樣的Linux核心程式只能使用printk函數輸出調用資訊。
在Linux 檔案系統中, /proc 經常被用來作為核心空間與使用者空間進行資料互動的工具。/proc檔案系統的行為方式與裝置檔案系統(/dev )類似。/proc 是虛擬檔案系統,也就是說了,/proc 並不
是真正的檔案系統,而是記憶體映射。所有讀寫/proc操作都是對記憶體的讀寫。 所以讀寫/proc 檔案系統的速度要遠比讀寫/dev 檔案系統的速度快。因此,/proc 檔案系統也可作為Linux 驅動與使用者空間程式互動的工具。有很多系統資訊就是通過/proc 檔案系統由核心空間的程式向外界提供的。
*在Linux 系統中提供了一類工具。通過這些工具,可以逐行跟蹤程式的代碼,就好像可視化開發工具的step into 和step over 一樣。
這些工具包含用於調試使用者空間程式的gdb、gdbserver 和調試核心空間程式的kgdb。
Android 深度探索(3)