<i id="bcuty"><sub id="bcuty"></sub></i>

<b id="bcuty"></b>

您的位置:首頁 >聚焦 >

Android Q 打通應用層到 HAL 層--( HAL 模塊實現)

2023-03-04 05:28:46    來源:程序員客棧

從這篇文章開始準備研究應用層到HAL層的一整套流程,目標是寫一個APP調用HAL的一個函數,在AOSP源碼環境下進行開發,大概流程是:APP---->Framework service---->native----->HAL

什么是HAL

HAL全稱Hardware Abstract Layer,硬件抽象層,它向下屏蔽了硬件的實現細節,向上提供了抽象接口,HAL是底層硬件和上層框架直接的接口,框架層通過HAL可以操作硬件設備,HAL的實現在用戶空間

為什么需要HAL

我們知道Android是基于Linux進行開發的,傳統的Linux對硬件的操作基本上都是在內核中,而Android把對硬件的操作分為了兩部分,HAL和內核驅動,HAL實現在用戶空間,驅動在內核空間,這是因為Linux內核中的代碼是需要開源的。


(資料圖片)

如果把對硬件的操作放在內核這會損害硬件廠商的利益,因為這是別人廠商的商業機密,而現在有了HAL層位于用戶空間,硬件廠商就可以將自己的核心算法之類的放在HAL層,保護了自己的利益,這樣Android系統才能得到更多廠商的支持

HAL實現的一般規則

每種硬件都對應了一個HAL模塊,要向實現自己的HAL,必須滿足HAL的相關規則,規則定義在源碼hardward目錄下,頭文件hardward.h,C文件hardward.c hardward.h中定義了三個重要的結構體:

structhw_module_t;structhw_module_methods_t;structhw_device_t;

其實HAL的實現使用了C中結構體繼承的技巧,當然這種繼承并不是真正意義的繼承,而是一種結構體強制轉換,我們可以理解為繼承

結構體hw_module_t代表HAL模塊,自己定義的HAL模塊必須包含一個自定義struct,且必須”繼承” hw_module_t(即第一個變量必須為 hw_module_t),且模塊的tag必須指定為HARDWARE_MODULE_TAG,代表這是HAL模塊的結構體

結構體hw_module_methods_t代表模塊的操作方法列表,它內部只有一個函數指針open,用來打開該模塊下的設備

結構體hw_device_t代表該模塊下的設備,自己定義的HAL模塊必須包含一個結構體,且必須“繼承” hw_device_t(即第一個變量必須為hw_device_t)

每個自定義HAL模塊還有一個模塊名和N個設備名(標識模塊下的設備個數,一個模塊可以有多個設備),

最后這個模塊定義好之后還必須導出符號HAL_MODULE_INFO_SYM,指向這個模塊,HAL_MODULE_INFO_SYM定義在hardware.h中值為“HMI”

接下來就手動實現一個HAL模塊,這個HAL提供一個加法函數 首先在hardware/libhardware/include/hardware目錄下創建hello.h文件

#include#include#include//HAL模塊名#defineHELLO_HARDWARE_MODULE_ID"hello"http://HAL版本號#defineHELLO_MODULE_API_VERSION_1_0HARDWARE_MODULE_API_VERSION(0,1)//設備名#defineHARDWARE_HELLO"hello"http://自定義HAL模塊結構體typedefstructhello_module{structhw_module_tcommon;}hello_module_t;//自定義HAL設備結構體typedefstructhello_device{structhw_device_tcommon;//加法函數int(*additionTest)(conststructhello_device*dev,inta,intb,int*total);}hello_device_t;//給外部調用提供打開設備的函數staticinlineint_hello_open(conststructhw_module_t*module,hello_device_t**device){returnmodule->methods->open(module,HARDWARE_HELLO,(structhw_device_t**)device);}

我們可以看到在hello.h中自定義了兩個結構體,分別繼承hw_module_t和hw_device_t且在第一個變量,滿足前面說的規則,另外定義在device結構體中的函數的第一個參數也必須是device結構體

接著在hardware/libhardware/modules/目錄下創建一個hello的文件夾,里面包含一個hello.c和Android.bp,我們來看hello.c文件

#defineLOG_TAG"HelloHal"#include#include#include#include#include#include//加法函數實現staticintadditionTest(conststructhello_device*dev,inta,intb,int*total){if(!dev){return-1;}*total=a+b;return0;}//關閉設備函數staticinthello_close(hw_device_t*dev){if(dev){free(dev);return0;}else{return-1;}}//打開設備函數staticinthello_open(consthw_module_t*module,constchar__unused*id,hw_device_t**device){if(device==NULL){ALOGE("NULLdeviceonopen");return-1;}hello_device_t*dev=malloc(sizeof(hello_device_t));memset(dev,0,sizeof(hello_device_t));dev->common.tag=HARDWARE_DEVICE_TAG;dev->common.version=HELLO_MODULE_API_VERSION_1_0;dev->common.module=(structhw_module_t*)module;dev->common.close=hello_close;dev->additionTest=additionTest;*device=&(dev->common);return0;}staticstructhw_module_methods_thello_module_methods={.open=hello_open,};//導出符號HAL_MODULE_INFO_SYM,指向自定義模塊hello_module_tHAL_MODULE_INFO_SYM={.common={.tag=HARDWARE_MODULE_TAG,.module_api_version=HELLO_MODULE_API_VERSION_1_0,.hal_api_version=HARDWARE_HAL_API_VERSION,.id=HELLO_HARDWARE_MODULE_ID,.name="DemoHelloHAL",.author="dongjiao.tang@tcl.com",.methods=&hello_module_methods,},};

前面我們說過,要想自定義的HAL模塊被外部使用需要導出符號HAL_MODULE_INFO_SYM,導出的這個模塊結構體最重要的其實就是tag,id和methods,tag必須定義為HARDWARE_MODULE_TAG,id就是在hello.h中定義的模塊名。

methods指向hello_module_methods地址,hello_module_methods指向結構體hw_module_methods_t,它里面的open函數指針作用是打開模塊下的設備,它指向hello_open函數

hello_open

staticinthello_open(consthw_module_t*module,constchar__unused*id,hw_device_t**device){if(device==NULL){ALOGE("NULLdeviceonopen");return-1;}hello_device_t*dev=malloc(sizeof(hello_device_t));memset(dev,0,sizeof(hello_device_t));dev->common.tag=HARDWARE_DEVICE_TAG;dev->common.version=HELLO_MODULE_API_VERSION_1_0;dev->common.module=(structhw_module_t*)module;dev->common.close=hello_close;dev->additionTest=additionTest;*device=&(dev->common);return0;}

其實hello_open函數也很簡單,就是創建一個hello_device_t結構體,這是我們自定義HAL模塊下的設備結構體,然后給這個結構體賦值,我們定義的加法函數additionTest賦值給設備結構體的函數指針,外部就可以使用。

因為這個HAL模塊導出了符號HAL_MODULE_INFO_SYM,所以我們能通過模塊id找到這個HAL模塊,有了模塊之后就可以調用它的hello_open函數打開設備,hello_open接收三個參數,module代表這個HAL模塊,id代表要打開的是這個模塊下哪一個設備,最后一個參數是一個二級指針,其實是我們自定義設備結構體指針的地址,傳個地址過來就可以給這個設備結構體賦值。

這樣最簡單的一個HAL模塊就定義好了,它僅僅提供了一個加法函數,對于Android.bp如下:最終編譯成一個共享lib庫

cc_library_shared{name:"hello.default",relative_install_path:"hw",proprietary:true,srcs:["hello.c"],header_libs:["libhardware_headers"],shared_libs:["liblog"],}

我們在根目錄執行如下命令來編譯一下:

mmmhardware/libhardware/modules/hello/

編譯成功后生成了一個hello.default.so共享lib庫

hello.default.so

我們將這個so庫push進手機/system/lib64路徑下,接著需要寫一個簡單測試程序來測一下

我們在HAL模塊目錄下再創建一個test目錄,test目錄下創建一個HelloTest.cpp測試文件和一個Android.bp文件,目錄結構如下:

在這里插入圖片描述
HelloTest.cpp源碼如下:

#include#include#include#defineTAG"HelloHal"intmain(){hello_device_t*hello_device=NULL;consthello_module_t*module=NULL;intret=hw_get_module(HELLO_HARDWARE_MODULE_ID,(conststructhw_module_t**)&module);if(!ret){ret=_hello_open((conststructhw_module_t*)module,&hello_device);}if(ret<0){ALOGD("gethello-halfailed.......");return-1;}inttotal=0;hello_device->additionTest(hello_device,3,5,&total);ALOGD("successstarthello-hal....total=%d",total);return0;}

通過hardware.c提供的hw_get_module函數,傳遞模塊名獲取對應的HAL模塊,接著調用我們自定義的_hello_open函數通過獲取到的模塊打開設備,獲取到設備之后,我們調用定義在設備結構體中的加法函數

Android.bp定義如下:

cc_binary{name:"hello_hal",srcs:["HelloTest.cpp"],shared_libs:["liblog","libhardware",],}

這個test會被編譯成二進制可執行文件:hello_hal

在這里插入圖片描述

我們將這個可執行文件push進手機system/bin目錄,然后執行

在這里插入圖片描述

可以看到log已經成功打?。?/p>

在這里插入圖片描述
到此我們最簡單的HAL就已經實現了,這個HAL并不設計底層驅動,實際開發中自己可以在設備結構體中定義函數訪問底層硬件。

后續文章會接著寫從native層通過HIDL訪問HAL,以及framework層通過JNI訪問native層,以及通過應用層APK通過AIDL訪問framework層,打通應用層到HAL層的整個開發框架。

原文鏈接: https://blog.csdn.net/qq_34211365/article/details/105492973

關鍵詞: 插入圖片 可執行文件 一個模塊

相關閱讀

巨胸护士在线播放视频二区

<i id="bcuty"><sub id="bcuty"></sub></i>

<b id="bcuty"></b>