OpenHarmony-HDF驅(qū)動(dòng)框架介紹及加載過(guò)程分析
??想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):??
??51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)??
前言
HarmonyOS面向萬(wàn)物互聯(lián)時(shí)代,而萬(wàn)物互聯(lián)涉及到了大量的硬件設(shè)備,這些硬件的離散度很高,它們的性能差異與配置差異都很大,所以這要求使用一個(gè)更靈活、功能更強(qiáng)大、能耗更低的驅(qū)動(dòng)框架。OpenHarmony系統(tǒng)HDF驅(qū)動(dòng)框架采用C語(yǔ)言面向?qū)ο缶幊棠P蜆?gòu)建,通過(guò)平臺(tái)解耦、內(nèi)核解耦,來(lái)達(dá)到兼容不同內(nèi)核,統(tǒng)一平臺(tái)底座的目的,從而幫助開發(fā)者實(shí)現(xiàn)驅(qū)動(dòng)一次開發(fā),多系統(tǒng)部署的效果。
1、HDF 驅(qū)動(dòng)框架
OpenHarmony 系統(tǒng) HDF 驅(qū)動(dòng)框架主要由驅(qū)動(dòng)基礎(chǔ)框架、驅(qū)動(dòng)程序、驅(qū)動(dòng)配置文件和驅(qū)動(dòng)接口這四個(gè)部分組成。
(1)HDF 驅(qū)動(dòng)基礎(chǔ)框架提供統(tǒng)一的硬件資源管理,驅(qū)動(dòng)加載管理以及設(shè)備節(jié)點(diǎn)管理等功能。驅(qū)動(dòng)框架采用的是主從模式設(shè)計(jì),由 Device Manager 和 Device Host 組成。Device Manager 提供了統(tǒng)一的驅(qū)動(dòng)管理,Device Manager 啟動(dòng)時(shí)根據(jù) Device Information 提供驅(qū)動(dòng)設(shè)備信息加載相應(yīng)的驅(qū)動(dòng) Device Host,并控制 Host 完成驅(qū)動(dòng)的加載。Device Host 提供驅(qū)動(dòng)運(yùn)行的環(huán)境,同時(shí)預(yù)置 Host Framework 與 Device Manager 進(jìn)行協(xié)同,完成驅(qū)動(dòng)加載和調(diào)用。根據(jù)業(yè)務(wù)的需求 Device Host 可以有多個(gè)實(shí)例。
(2)驅(qū)動(dòng)程序?qū)崿F(xiàn)驅(qū)動(dòng)具體的功能,每個(gè)驅(qū)動(dòng)由一個(gè)或者多個(gè)驅(qū)動(dòng)程序組成,每個(gè)驅(qū)動(dòng)程序都對(duì)應(yīng)著一個(gè) Driver Entry。Driver Entry 主要完成驅(qū)動(dòng)的初始化和驅(qū)動(dòng)接口綁定功能。
(3)驅(qū)動(dòng)配置文件.hcs 主要由設(shè)備信息(Device Information)和設(shè)備資源(Device Resource)組成。Device Information 完成設(shè)備信息的配置。如配置接口發(fā)布策略,驅(qū)動(dòng)加載的方式等。Device Resource 完成設(shè)備資源的配置。如 GPIO 管腳、寄存器等資源信息的配置。
(4)驅(qū)動(dòng)接口 HDI(Hardware Driver interface )提供標(biāo)準(zhǔn)化的接口定義和實(shí)現(xiàn),驅(qū)動(dòng)框架提供 IO Service和IO Dispatcher 機(jī)制,使得不同部署形態(tài)下驅(qū)動(dòng)接口趨于形式一致。
HDF框架以組件化的驅(qū)動(dòng)模型作為核心設(shè)計(jì)思路,為開發(fā)者提供更精細(xì)化的驅(qū)動(dòng)管理,讓驅(qū)動(dòng)開發(fā)和部署更加規(guī)范。HDF框架將一類設(shè)備驅(qū)動(dòng)放在同一個(gè)host里面,開發(fā)者也可以將驅(qū)動(dòng)功能分層獨(dú)立開發(fā)和部署,支持一個(gè)驅(qū)動(dòng)多個(gè)node,HDF驅(qū)動(dòng)模型如下圖所示:
2、HDF 驅(qū)動(dòng)開發(fā)
基于HDF框架進(jìn)行驅(qū)動(dòng)的開發(fā)主要分為兩個(gè)部分,驅(qū)動(dòng)實(shí)現(xiàn)和驅(qū)動(dòng)配置,詳細(xì)開發(fā)流程如下所示:
2.1 驅(qū)動(dòng)實(shí)現(xiàn)
驅(qū)動(dòng)實(shí)現(xiàn)包含驅(qū)動(dòng)業(yè)務(wù)代碼和驅(qū)動(dòng)入口注冊(cè)。
2.1.1 驅(qū)動(dòng)業(yè)務(wù)代碼
//驅(qū)動(dòng)對(duì)外提供的服務(wù)能力,將相關(guān)的服務(wù)接口綁定到HDF框架。
int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
{
HDF_LOGD("Sample driver bind success");
return 0;
}
// 驅(qū)動(dòng)自身業(yè)務(wù)初始的接口。
int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
{
HDF_LOGD("Sample driver Init success");
return 0;
}
// 驅(qū)動(dòng)資源釋放的接口。
void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
{
HDF_LOGD("Sample driver release success");
return;
}
2.1.2驅(qū)動(dòng)入口注冊(cè)到HDF框架
// 定義驅(qū)動(dòng)入口的對(duì)象,必須為HdfDriverEntry(在hdf_device_desc.h中定義)類型的全局變量
struct HdfDriverEntry g_sampleDriverEntry =
{
.moduleVersion = 1,
.moduleName = "sample_driver",
.Bind = HdfSampleDriverBind,
.Init = HdfSampleDriverInit,
.Release = HdfSampleDriverRelease,
};
HDF_INIT(g_sampleDriverEntry);
// 調(diào)用HDF_INIT將驅(qū)動(dòng)入口注冊(cè)到HDF框架中,在加載驅(qū)動(dòng)時(shí)HDF框架會(huì)先調(diào)用Bind函數(shù),再調(diào)用Init函數(shù)加載該驅(qū)動(dòng),當(dāng)Init調(diào)用異常時(shí),HDF框架會(huì)調(diào)用Release釋放驅(qū)動(dòng)資源并退出。
2.2 驅(qū)動(dòng)配置
驅(qū)動(dòng)配置包含兩部分,HDF框架定義的驅(qū)動(dòng)設(shè)備描述和驅(qū)動(dòng)的私有配置信息。HDF使用HCS作為配置描述源碼,內(nèi)容以 Key-Value 鍵值對(duì)為主要形式。它實(shí)現(xiàn)了配置代碼與驅(qū)動(dòng)代碼解耦,便于開發(fā)者進(jìn)行配置管理。HC-GEN (全稱 HDF Configuration Generator) 是 HCS 配置轉(zhuǎn)換工具,可以將 HDF 配置文件轉(zhuǎn)換為軟件可讀取的文件格式:在弱性能環(huán)境中,轉(zhuǎn)換為配置樹源碼,驅(qū)動(dòng)可直接調(diào)用 C代碼獲取配置;在高性能環(huán)境中,轉(zhuǎn)換為 HCB(HDF Configuration Binary)二進(jìn)制文件,驅(qū)動(dòng)可使用 HDF框架提供的配置解析接口獲取配置。
HCS經(jīng)過(guò)HC-GEN編譯生成HCB文件,HDF驅(qū)動(dòng)框架中的HCS Parser模塊會(huì)從HCB文件中重建配置樹,HDF驅(qū)動(dòng)模塊使用HCS Parser提供的配置讀取接口獲取配置內(nèi)容。驅(qū)動(dòng)配置過(guò)程的原理圖如下所示:
2.2.1 驅(qū)動(dòng)設(shè)備描述(必選)
HDF框架加載驅(qū)動(dòng)所需要的信息來(lái)源于HDF框架定義的驅(qū)動(dòng)設(shè)備描述,因此基于HDF框架開發(fā)的驅(qū)動(dòng)必須要在HDF框架定義的device_info.hcs配置文件中添加對(duì)應(yīng)的設(shè)備描述,驅(qū)動(dòng)的設(shè)備描述填寫如下所示:
sample_host :: host{
hostName = "host0"; //host名稱,host節(jié)點(diǎn)是用來(lái)存放某一類驅(qū)動(dòng)的容器。
priority = 100; //host啟動(dòng)優(yōu)先級(jí)(0-200),值越大優(yōu)先級(jí)越低,建議默認(rèn)配100,優(yōu)先級(jí)相同則不保證host的加載順序。
device_sample :: device { //sample設(shè)備節(jié)點(diǎn)。
device0 :: deviceNode { //sample驅(qū)動(dòng)的DeviceNode節(jié)點(diǎn)。
policy = 1; //驅(qū)動(dòng)服務(wù)發(fā)布的策略
priority = 100; //驅(qū)動(dòng)啟動(dòng)優(yōu)先級(jí)(0-200),值越大優(yōu)先級(jí)越低,建議默認(rèn)配 100,優(yōu)先級(jí)相同則不保證 device 的加載順序
preload = 0; //驅(qū)動(dòng)按需加載字段
permission = 0664;//驅(qū)動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn)權(quán)限
moduleName = "sample_driver"; //驅(qū)動(dòng)名稱,該字段的值必須和驅(qū)動(dòng)入口結(jié)構(gòu)的moduleName值一致
serviceName = "sample_service"; //驅(qū)動(dòng)對(duì)外發(fā)布服務(wù)的名稱,必須唯一
deviceMatchAttr = "sample_config";//驅(qū)動(dòng)私有數(shù)據(jù)匹配的關(guān)鍵字,必須和驅(qū)動(dòng)私有數(shù)據(jù)配置表中的match_attr值相等。
}
}
}
2.2.2驅(qū)動(dòng)私有配置信息(可選)
如果驅(qū)動(dòng)有私有配置,則可以添加一個(gè)驅(qū)動(dòng)的配置文件,用來(lái)填寫一些驅(qū)動(dòng)的默認(rèn)配置信息,HDF框架在加載驅(qū)動(dòng)的時(shí)候,會(huì)將對(duì)應(yīng)的配置信息獲取并保存在HdfDeviceObject 中的property里面,通過(guò)Bind和Init傳遞給驅(qū)動(dòng),驅(qū)動(dòng)的配置信息示例如下:
root {
SampleDriverConfig {
sample_version = 1;
sample_bus = "I2C_0";
match_attr = "sample_config"; //該字段的值必須和device_info.hcs中的deviceMatchAttr值一致
}
}
3、HDF 驅(qū)動(dòng)加載
HDF驅(qū)動(dòng)加載包括按需加載和按序加載。按需加載是HDF框架支持驅(qū)動(dòng)在系統(tǒng)啟動(dòng)過(guò)程中默認(rèn)加載,或者在系統(tǒng)啟動(dòng)之后動(dòng)態(tài)加載;按序加載是HDF框架支持驅(qū)動(dòng)在系統(tǒng)啟動(dòng)的過(guò)程中按照驅(qū)動(dòng)的優(yōu)先級(jí)進(jìn)行加載。HDF框架定義的驅(qū)動(dòng)按需加載方式的策略是由配置文件中的 preload 字段來(lái)控制,preload 字段的取值范圍以及含義如下:
驅(qū)動(dòng)的按序加載是通過(guò)配置文件中的 priority(取值范圍為整數(shù) 0 到 200)來(lái)決定的,priority 值越小,表示的優(yōu)先級(jí)越高。驅(qū)動(dòng)的加載順序,優(yōu)先根據(jù) host 的 priority 決定,如果host 的 priority 相同,再根據(jù) host 內(nèi)的驅(qū)動(dòng) priority 值來(lái)決定加載順序。
3.1 HDF_INIT宏展開
驅(qū)動(dòng)入口注冊(cè)到HDF框架,會(huì)調(diào)用HDF_INIT函數(shù)將驅(qū)動(dòng)入口地址注冊(cè)到HDF框架。
#define HDF_SECTION__attribute__((section(“.hdf.driver”)))
#define HDF_DRIVER_INIT(module) \
const size_t USED_ATTR module##HdfEntry HDF_SECTION = (size_t)(&(module))
可以看到 HDF_INIT 宏是定義了一個(gè)“驅(qū)動(dòng)模塊名+HdfEntry”的符號(hào)放到".hdf.driver"所在 section,該符號(hào)指向的內(nèi)存地址即為驅(qū)動(dòng)程序入口結(jié)構(gòu)體的地址。這個(gè)特殊的 section 將用于開機(jī)啟動(dòng)時(shí)查找設(shè)備驅(qū)動(dòng)。
3.2獲取驅(qū)動(dòng)列表
HDF驅(qū)動(dòng)框架通過(guò)將驅(qū)動(dòng)程序入口符號(hào)的地址集中存放到一個(gè)特殊的 section 來(lái)實(shí)現(xiàn)對(duì)驅(qū)動(dòng)的索引,這個(gè)section的開頭和末尾插入了_hdf_drivers_start、_hdf_drivers_end兩個(gè)特殊符號(hào),用于標(biāo)記這個(gè) section 的范圍,兩個(gè)特殊符號(hào)之間的數(shù)據(jù)即為驅(qū)動(dòng)實(shí)現(xiàn)指針。
3.3獲取設(shè)備列表
配置文本編譯后會(huì)變成二進(jìn)制格式的配置文件,其中設(shè)備相關(guān)信息被存放在一個(gè)用“hdf_manager”標(biāo)記的 device_info 配置塊中,host的內(nèi)容以塊的形式在device_info 塊中依次排列,host塊中記錄了host名稱、啟動(dòng)優(yōu)先級(jí)和設(shè)備列表信息。設(shè)備信息中的 moduleName字段將用于和驅(qū)動(dòng)程序入口中的moduleName進(jìn)行匹配,從而為設(shè)備匹配到正確的驅(qū)動(dòng)程序,完成設(shè)備與驅(qū)動(dòng)的匹配,具體流程圖如下:
3.4驅(qū)動(dòng)框架啟動(dòng)
late_initcall宏展開。
__define_initcall宏展開。
___define_initcall宏展開。
宏含義:
(1)聲明一個(gè)類型為initcall_t,名稱為__initcall_DeviceManagerInit的函數(shù)指針。
(2)將這個(gè)函數(shù)指針初始化為DeviceManagerInit。
(3)編譯的時(shí)候需要把這個(gè)函數(shù)指針變量放置到名稱為“.initcall7.init”的section中,其實(shí)質(zhì)就是將這個(gè)函數(shù)DeviceManagerInit的首地址放置到了這個(gè).initcall7.init的section中。
內(nèi)核初始化的內(nèi)存圖:
其中__init用來(lái)標(biāo)示的是初始化函數(shù),在初始化后不會(huì)再調(diào)用,__initdata是初始化數(shù)據(jù),__initparam是初始化參數(shù),其他7個(gè)初始化宏就是初始化函數(shù)會(huì)用到的,初始化的時(shí)候按。
照.initcall1.init->.initcall7.init的順序初始化。do_basic_setup執(zhí)行.initcall1.init->.initcall7.init的順序初始化。
4、總結(jié)
(1)在系統(tǒng)啟動(dòng)時(shí),DeviceManagerInit通過(guò)late_initcall先啟動(dòng)。
(2) Device Manager 根據(jù) Device Information 信息,解析配置文件中的 Host 列表,根據(jù) Host 列表中的信息來(lái)實(shí)例化對(duì)應(yīng)的 Host 對(duì)象。
(3)Host遍歷設(shè)備列表去獲取與之匹配的驅(qū)動(dòng)程序名稱,然后基于驅(qū)動(dòng)程序名稱遍歷.hdf.driver section 獲得驅(qū)動(dòng)程序地址。
(4)設(shè)備與驅(qū)動(dòng)匹配成功之后,獲取指定驅(qū)動(dòng)的入口地址,加載對(duì)應(yīng)的設(shè)備驅(qū)動(dòng)程序。
()調(diào)用指定驅(qū)動(dòng)的 Bind 接口,用于關(guān)聯(lián)設(shè)備和服務(wù)實(shí)例。
(6)調(diào)用指定驅(qū)動(dòng)的 Init 接口,用于完成驅(qū)動(dòng)的相關(guān)初始化工作。
(7)如果驅(qū)動(dòng)被卸載或者因?yàn)橛布仍?Init 接口返回失敗,Release 將被調(diào)用,用于釋放驅(qū)動(dòng)申請(qǐng)的各類資源。
??想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):??
??51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)??