Android Hal層(即 Hardware Abstraction Layer)是Google開發的Android系統裏上層應用對底層硬件操做屏蔽的一個軟件層次,說直白點,就是上層應用沒必要關心底層硬件具體是如何工做的,只須要調用底層提供的統一接口便可,這種設計思想普遍的存在於當前的軟件的架構設計裏。我的感受,之前在Linux系統下學習驅動程序的開發時,當驅動程序完成時,咱們會編寫相應的用戶空間測試程序,感受這就有點相似此處的硬件抽象層的工做,只不過原來是把測試程序用交叉工具鏈編譯成可執行程序而後下載到開發板上進行驗證,而在Android的硬件抽象層中,將測試程序再進行了一次封裝,把測試接口封裝起來傳遞給上一層調用,這樣就能隱藏硬件的實現細節和參數。android
其實Android系統裏徹底能夠沒有HAL硬件抽象層,上層應用能夠直接經過API調用到底層硬件,可是Android自出現一直打着開源的旗號,而一些硬件設備廠商因爲商業因素,不但願把本身的核心代碼開源出來,而只是提供二進制的代碼。另外,Android系統裏使用的一些硬件設備接口可能不是使用的Linux Kernel的統一接口,而且還有GPL版權的緣由,因此Google在Android架構裏提出了Hal的概念,這個HAL其實就是獨立的意思,Android系統不只依賴於某一個具體的硬件驅動,而是依賴於Hal代碼,這樣,第三方廠商能夠將本身不開源的代碼封裝在HAL層,僅僅提供二進制代碼。在具體分析Android硬件抽象層以前,先從下圖瞭解下其在整個Android系統架構中所處的位置:api
Android Hal架構分爲兩種:數組
①舊的架構module架構
②新的架構module stub框架
下面咱們就具體分析下兩種架構各自的特色:ide
一 Module架構函數
Android用戶應用程序或者框架層代碼由JAVA實現,Java運行在Dalvik虛擬機中,沒有辦法直接訪問底層硬件,只能經過調用so本地庫代碼實現,在so本地代碼裏有對底層硬件操做的代碼,以下圖所示:工具
能夠這樣說,應用層或者框架層Java代碼,經過JNI技術調用C或C++寫的so庫代碼,在so庫代碼中調用底層驅動,從而實現上層應用操做底層硬件的目的。實現硬件操做的so庫爲module.學習
其實現流程以下圖所示:測試
這種設計架構雖然知足了Java應用訪問硬件的須要,可是,使得咱們的代碼上下層次間的耦合過高,用戶程序或者框架代碼必需要去加載module庫,若是底層硬件有變化,module要重新編譯,上層也要作相應變化,另外,若是多個應用程序同時訪問硬件,都去加載module,同一module被多個進程映射屢次,會有代碼的重入問題。
二 新的Hal架構:
新的代碼架構使用的是module stub方式.Stub是存根或者樁的意思,其實說白了,就是指一個對象表明的意思。上層應用層或者框架層代碼加載so庫代碼,so庫代碼咱們稱之爲module,在Hal層註冊了每一個硬件對象的存根stub,當上層須要訪問硬件的時候,就從當前註冊的硬件對象stub裏查找,找到以後stub會向上層module提供該硬件對象的operations interface(操做接口),該操做接口就保存在module中,上層應用或框架層再經過這個module操做接口來訪問硬件。其架構以下:
以上分別介紹了Module架構和Stub架構,下面作一個對比:
在Module架構中,本地代碼由so庫實現,上層直接將so庫映射到進程空間,會有代碼重入及設備屢次打開的問題。新的Stub框架雖然也要加載module庫,可是這個module已經不包含操做底層硬件驅動的功能了,它裏面保存的只是底層stub提供的操做接口,底層stub扮演了「接口提供者」的角色,當stub第一次被使用時加載到內存,後續再使用時僅返回硬件對象操做接口,不會存在設備屢次打開的問題,而且因爲多進程訪問時返回的只是函數指針,代碼並無重入。
三 Hal Stub框架分析
Hal Stub的框架比較簡單,三個結構體、兩個常量、一個函數,簡稱321架構,它的定義在:
alps/hardware/libhardware/include/hardware/hardware.h
alps/hardware/libhardware/hardware.c
下面咱們先看下三個重要的結構體,其包含在hardware.h中:
上述三個結構體之間關係緊密,每一個硬件對象都由hw_module_t來描述,只要咱們拿到了這個硬件對象,就能夠調用它的open方法,返回這個硬件對象的硬件操做接口,而後就能夠經過這些硬件操做接口來間接操做硬件。只不過,open方法被hw_module_methods_t結構封裝了一次,硬件操做接口被hw_device_t封裝了一次而已。下面這張圖能夠反映出它們三者的關係:
- /**
- *每個硬件都經過hw_module_t來描述,咱們稱之爲一個硬件對象。你能夠去"繼承"這個hw_module_t
- *而後擴展本身的屬性,硬件對象必須定義爲一個固定的名字HMI,即:Hardware Module Information的簡寫
- *每一個硬件對象裏都封裝了一個函數指針open用於打開硬件,咱們理解爲硬件對象的open方法,open調用後
- *返回這個硬件對應的操做接口集合
- */
- typedef struct hw_module_t {
- /** tag must be initialized to HARDWARE_MODULE_TAG */
- uint32_t tag; //該值必須聲明爲HARDWARE_MODULE_TAG
- /**
- * The API version of the implemented module. The module owner is
- * responsible for updating the version when a module interface has
- * changed.
- *
- * The derived modules such as gralloc and audio own and manage this field.
- * The module user must interpret the version field to decide whether or
- * not to inter-operate with the supplied module implementation.
- * For example, SurfaceFlinger is responsible for making sure that
- * it knows how to manage different versions of the gralloc-module API,
- * and AudioFlinger must know how to do the same for audio-module API.
- *
- * The module API version should include a major and a minor component.
- * For example, version 1.0 could be represented as 0x0100. This format
- * implies that versions 0x0100-0x01ff are all API-compatible.
- *
- * In the future, libhardware will expose a hw_get_module_version()
- * (or equivalent) function that will take minimum/maximum supported
- * versions as arguments and would be able to reject modules with
- * versions outside of the supplied range.
- */
- uint16_t module_api_version;
- #define version_major module_api_version
- /**
- * version_major/version_minor defines are supplied here for temporary
- * source code compatibility. They will be removed in the next version.
- * ALL clients must convert to the new version format.
- */
- /**
- * The API version of the HAL module interface. This is meant to
- * version the hw_module_t, hw_module_methods_t, and hw_device_t
- * structures and definitions.
- *
- * The HAL interface owns this field. Module users/implementations
- * must NOT rely on this value for version information.
- *
- * Presently, 0 is the only valid value.
- */
- uint16_t hal_api_version;
- #define version_minor hal_api_version
- /** Identifier of module */
- const char *id; //硬件id名,惟一標識module
- /** Name of this module */
- const char *name; //硬件module的名字
- /** Author/owner/implementor of the module */
- const char *author; //做者
- /** Modules methods */
- //指向封裝有open函數指針的結構體
- struct hw_module_methods_t* methods;
- /** module's dso */
- void* dso;
- /** padding to 128 bytes, reserved for future use */
- //128字節補齊
- uint32_t reserved[32-7];
- } hw_module_t;
- //硬件對象的open方法描述結構體,它裏面只有一個元素:open函數指針
- typedef struct hw_module_methods_t {
- /** Open a specific device */
- int (*open)(const struct hw_module_t* module, const char* id,
- struct hw_device_t** device);
- } hw_module_methods_t;
- /**
- * Every device data structure must begin with hw_device_t
- * followed by module specific public methods and attributes.
- */
- //硬件對象hw_module_t的open方法返回該硬件的Operation interface,它由hw_device_t結構體來描述
- //咱們稱之爲該硬件的操做接口
- typedef struct hw_device_t {
- /** tag must be initialized to HARDWARE_DEVICE_TAG */
- uint32_t tag; //必須賦值爲HARDWARE_DEVICE_TAG
- /**
- * Version of the module-specific device API. This value is used by
- * the derived-module user to manage different device implementations.
- *
- * The module user is responsible for checking the module_api_version
- * and device version fields to ensure that the user is capable of
- * communicating with the specific module implementation.
- *
- * One module can support multiple devices with different versions. This
- * can be useful when a device interface changes in an incompatible way
- * but it is still necessary to support older implementations at the same
- * time. One such example is the Camera 2.0 API.
- *
- * This field is interpreted by the module user and is ignored by the
- * HAL interface itself.
- */
- uint32_t version; //版本號
- /** reference to the module this device belongs to */
- struct hw_module_t* module; //該設備屬於哪一個硬件對象,能夠當作硬件操做接口與硬件對象的聯繫
- /** padding reserved for future use */
- uint32_t reserved[12]; //字節補齊
- /** Close this device */
- int (*close)(struct hw_device_t* device); //該設備的關閉函數指針
- } hw_device_t;
接下來在看321架構中的:兩個符號常量和一個函數:
- //HAL Stub對象固定的名字
- #define HAL_MODULE_INFO_SYM HMI
- /**
- * Name of the hal_module_info as a string
- */
- //字符串形式的名字
- #define HAL_MODULE_INFO_SYM_AS_STR "HMI"
- /**
- * Get the module info associated with a module by id.
- *
- * @return: 0 == success, <0 == error and *module == NULL
- */
- //經過硬件名來得到硬件HAL Stub對象
- int hw_get_module(const char *id, const struct hw_module_t **module);
用戶程序經過硬件的id名來拿到硬件,下面咱們以android平臺下驅動的開發及測試框架概述(二)一文的例子來分析:註冊一個硬件對象的方法其實只須要聲明一個結構體便可,以註冊freg_module_t爲例:
咱們只須要聲明一個結構體freg_module_t,起名爲HAL_MODULE_INFO_SYM,也就是固定的名字:HMI,而後將這個結構體填充好就行。
- struct freg_module_t HAL_MODULE_INFO_SYM = {
- common: {
- tag: HARDWARE_MODULE_TAG,
- version_major: 1,
- version_minor: 0,
- id: FREG_HARDWARE_MODULE_ID,
- name: MODULE_NAME,
- author: MODULE_AUTHOR,
- methods: &freg_module_methods,
- }
- //擴展屬性
- };
- 而freg_module_t結構是「繼承」的hw_module_t類型,建立本身的硬件對象,而後擴展本身的特有屬性。
- <pre name="code" class="cpp">struct freg_module_t {
- struct hw_module_t common;
- };
上面的methods被初始化爲freg_module_methods的地址,其結構爲hw_methods_t類型的,其聲明代碼以下:其僅有的open成員是個函數指針,它被指向freg_device_open函數:
- static struct hw_module_methods_t freg_module_methods = {
- open: freg_device_open
- };
這個open函數主要作了如下幾件事:
- static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device) {
- if(!strcmp(id, FREG_HARDWARE_DEVICE_ID)) {
- struct freg_device_t* dev;
- dev = (struct freg_device_t*)malloc(sizeof(struct freg_device_t));
- if(!dev) {
- LOGE("Failed to alloc space for freg_device_t.");
- return -EFAULT;
- }
- memset(dev, 0, sizeof(struct freg_device_t));
- dev->common.tag = HARDWARE_DEVICE_TAG;
- dev->common.version = 0;
- dev->common.module = (hw_module_t*)module;
- dev->common.close = freg_device_close;
- dev->set_val = freg_set_val;
- dev->get_val = freg_get_val;
- if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
- LOGE("Failed to open device file /dev/freg -- %s.", strerror(errno));
- free(dev);
- return -EFAULT;
- }
- *device = &(dev->common);
- LOGI("Open device file /dev/freg successfully.");
- return 0;
- }
- return -EFAULT;
- }
1:分配硬件設備操做結構體freg_device_t,其描述了硬件操做行爲
2:初始化freg_device_t的父結構體hw_device_t成員
3:初始化了freg_device_t中的擴展的操做接口
4:打開設備,將freg_device_t結構體以父結構體類型返回
其中freg_device_t和父結構體hw_device_t的關係:
上面所涉及的擴展接口再也不作進一步分析,其主要做用是直接和底層驅動打交道。
- struct freg_device_t {
- struct hw_device_t common;
- int fd;
- int (*set_val)(struct freg_device_t* dev, int val);
- int (*get_val)(struct freg_device_t* dev, int* val);
- };
小結一下:咱們有一個硬件id名,經過這個id調用hw_get_module(const char *id, const struct hw_module_t **module)這個函數查找註冊到當前系統中與id對應的硬件對象並返回,硬件對象裏有個經過hw_module_methods_t結構體封裝的open函數指針,回調這個open函數,它返回封裝有硬件操做接口的freg_device_t結構體,這樣咱們就能夠經過這個硬件接口去間接訪問硬件了。能夠用下面的圖來描述這個過程:
下面咱們再來看hw_get_module這個函數的具體實現,其實如今hardware.c中:
- static const char *variant_keys[] = {
- 「ro.hardware」,
- 「ro.product.board」,
- 「ro.board.platform」,
- 「ro.arch」
- };
- // 由上面定義的字符串數組可知,HAL_VARIANT_KEYS_COUNT的值爲4
- struct constint HAL_VARIANT_KEYS_COUNT = (sizeof(variant_keys)/sizeof(variant_keys[0]));
- int hw_get_module(const char *id, const struct hw_module_t **module){
- // 調用3個參數的hw_get_module_by_class函數
- return hw_get_module_by_class(id, NULL, module);
- }
- int hw_get_module_by_class(const char *class_id, const char *inst,
- const struct hw_module_t **module){
- int status;
- int i;
- // 聲明一個hw_module_t指針變量hmi
- const struct hw_module_t *hmi = NULL;
- char prop[PATH_MAX};
- char path[PATH_MAX];
- char name[PATH_MAX];
- // 由前面調用函數可知,inst = NULL,執行else部分,將硬件id名拷貝到name數組裏
- if(inst)
- snprintf(name, PATH_MAX, 「%s.%s」, class_id, inst);
- else
- strlcpy(name, class_id, PATH_MAX);
- // i 循環5次
- for(i=0; i<HAL_VARIANT_KEYS_COUNT+1; i++){
- if(i<HAL_VARIANT_KEYS_COUNT){
- // 從系統屬性裏依次查找前面定義的4個屬性的值,找其中一個後,執行後面代碼,找不到,進入else部分執行
- if(property_get(variant_keys[i], prop, NULL) == 0){
- continue;
- }
- // 找到一個屬性值prop後,拼寫path的值爲:/vendor/lib/hw/硬件id名.prop.so
- snprintf(path, sizeof(path), 「%s/%s.%s.so」,
- HAL_LIBRARY_PATH2, name, prop);
- if(access(path, R_OK) ==0) break; // 若是path指向有效的庫文件,退出for循環
- // 若是vendor/lib/hw目錄下沒有庫文件,查找/system/lib/hw目錄下有沒有:硬件id名.prop.so的庫文件
- snprintf(path, sizeof(path), 「%s/%s.%s.so」,
- HAL_LIBRARY_PATH1, name, prop);
- If(access(path, R_OK) == 0) break;
- } else {
- // 若是4個系統屬性都沒有定義,則使用默認的庫名:/system/lib/hw/硬件id名.default.so
- snprintf(path, sizeof(path), 「%s/%s.default.so」,
- HAL_LIBRARY_PATH1, name);
- If(access(path, R_OK) == 0) break;
- }
- }
- status = -ENOENT;
- if(i<HAL_VARIANT_KEYS_COUNT+1){
- status = load(class_id, path, module); // 難道是要加載前面查找到的so庫??
- }
- return status;
- }
- static int load(const char *id, counst char *path, const struct hw_module_t **pHmi){
- void *handle;
- struct hw_module_t * hmi;
- // 經過dlopen打開so庫
- handle = dlopen(path, RTLD_NOW);
- // sym的值爲」HMI」,這個名字還有印象嗎?
- const char * sym = HAL_MODULE_INFO_SYM_AS_STR;
- // 經過dlsym從打開的庫裏查找」HMI」這個符號,若是在so代碼裏有定義的函數名或變量名爲HMI,dlsym返回其地址hmi,將該地址轉化成hw_module_t類型,即,硬件對象,這招夠狠,「殺雞取卵」
- hmi = (struct hw_module_t *)dlsym(handle, sym);
- // 判斷找到的硬件對象的id是否和要查找的id名一致,不一致出錯退出
- // 取了卵還要驗證下是否是本身要的「卵」
- if(strcmp(id, hmi->) != 0){
- // 出錯退出處理
- }
- // 將庫的句柄保存到hmi硬件對象的dso成員裏
- hmi->dso = handle;
- // 將硬件對象地址送給load函數者,最終將硬件對象返回到了hw_get_module的調用者
- *pHmi = hmi;
- // 成功返回
- }
經過上面代碼的註釋分析可知,硬件對象聲明的結構體代碼被編譯成了so庫,因爲該結構體聲明爲const類型,被so庫包含在其靜態代碼段裏,要找到硬件對象,首先要找到其對應的so庫,再經過dlopen,dlsym這種「殺雞取卵」的方式找到硬件對象,固然這兒的:「雞」是指:so庫,「卵」即硬件對象led_module_t結構。
在聲明結構體freg_module_t時,其名字統必定義爲了HMI,而這麼作的目的就是爲了經過dlsym來查找Freg HAL Stub源碼生成的so庫裏的」HMI」符號。如今很明顯了,咱們寫的HAL Stub代碼最終要編譯so庫文件,而且庫文件名爲:freg.default.so(固然能夠設置四個系統屬性之一來指定名字爲:freg.屬性值.so),而且庫的所在目錄爲:/system/lib/hw/。
Android Hal層簡要分析大體都這樣了。