Android 深度探索(3)

第八章 讓開發板發出聲音:蜂鳴器驅動linux

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。ui

extern void modul e_put (struct module *module);指針

第2 種狀況的問題根源就是卸載函數,所以,只要將原來的卸載函數替換成一個空的卸載函數便可。調試

第九章 硬件抽象層:HALserver

    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。

相關文章
相關標籤/搜索