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