文章大綱
- 引言
- 一、HDF的驅動加載(驅動安裝)方式
- 1、動態加載(主要是uhdf)
- 2、靜態加載(主要是khdf)
- 2.1、驅動入口實現
- 2.1.1、Bind接口
- 2.1.2、Init接口
- 2.1.3、Release接口
- 2.2、HDF_INIT 驅動入口符號
- 2.3、獲取驅動列表
- 2.4、獲取設備列表
- 2.5、設備與驅動的匹配
- 2.6、HDF驅動框架的啟動
- 2.7、驅動靜態加載的全流程總結
- 二、驅動框架交互流程
- 三、驅動的開發
- 3.1、實現驅動的業務功能邏輯的三個接口
- 3.2、定義用于注冊驅動入口對象的結構體DriverEntry
- 3.3、驅動配置信息
- 3.3.1、驅動設備描述(必選)
- 3.3.2、驅動私有配置信息(可選)
- 3.3.3、配置SeLinux
引言
前面HDF 驅動框架基礎理論入門文章簡單的總結了HDF最基礎的理論知識,接下來好好學習下HDF的一些核心工作原理。
一、HDF的驅動加載(驅動安裝)方式
1、動態加載(主要是uhdf)
傳統so方式,先dlopen打開指定的so,驅動程序通過指定symbol方式找到驅動函數入口進行加載。
2、靜態加載(主要是khdf)
OpenHarmony驅動主要部署在內核態,當前主要采用靜態鏈接方式,隨內核子系統編譯和系統鏡像打包。HDF驅動加載包括按需加載和按序加載。按需加載是HDF框架支持驅動在系統啟動過程中默認加載,或者在系統啟動之后動態加載;按序加載是HDF框架支持驅動在系統啟動的過程中按照驅動的優先級進行加載。HDF框架定義的驅動按需加載方式的策略是由配置文件中的 preload 字段來控制,preload 字段的取值范圍以及含義如下:
驅動的按序加載是通過配置文件中的 priority(取值范圍為整數 0 到 200)來決定的,priority 值越小,表示的優先級越高。驅動的加載順序,優先根據 host 的 priority 決定,如果host 的 priority 相同,再根據 host 內的驅動 priority 值來決定加載順序。
采用將驅動程序通過Scatter編譯到指定的Section,再通過訪問指定Section的地址找到驅動函數入口進行加載,如下是內核態驅動靜態加載具體流程:
2.1、驅動入口實現
在HDF驅動框架中,HdfDriverEntry對象用于描述驅動入口的實現:
struct HdfDriverEntry {int32_t moduleVersion;const char *moduleName;int32_t (*Bind) (struct HdfDeviceObject *device0bject) ;int32_t (*Init) (struct HdfDeviceObject *device0bject) ;void (*Release) (struct HdfDevice0bject *device0bject) ;
}
2.1.1、Bind接口
用于驅動接口實例化綁定,若需要發布驅動接口,會在驅動加載過程中被調用,實例化該接口的驅動服務和DeviceObject綁定。驅動對外提供的服務能力,將相關的服務接口綁定到HDF框架。該接口的作用主要是完成驅動設備和設備服務接口的bind動作。
在HdfDriverEntry的Bind方法中,必須完成全部驅動服務接口的綁定,禁止將服務接口未定義或定義為空,因為驅動定義的服務接口,均是對外暴露的,如果未定義或定義為空,可能會導致外部調用時產生異常,從而降低驅動的可靠性。
2.1.2、Init接口
當框架完成設備綁定動作后,就開始調用驅動初始化接口,初始化成功后,驅動框架根據配置文件決定是對外創建設備服務接口,還是接口只對當前服務可見。如果Init初始化失敗,驅動框架就會主動釋放創建的設備接口等信息。
2.1.3、Release接口
用于驅動的卸載,在該接口中需要釋放驅動實例的軟硬件資源。當需要卸載驅動時,驅動框架先通過該接口通知驅動程序釋放資源,然后再釋放其他內部資源。
2.2、HDF_INIT 驅動入口符號
系統啟動時會執行HDF_INIT宏,
int SampleDriverBind(struct HdfDevice0bject *device0bject)
int SampleDriverInit (struct HdfDeviceObject *device0bject)
void SampleDriverRelease(struct HdfDeviceObject *device0bject)struct HdfDriverEntry g_sampleDriverEntry = {.moduleVersion = 1,.moduleName = "sample_driver",.Bind = SampleDriverBind, // 職責:綁定驅動對外提供的服務接口到HDF.Init = SampleDriverInit, // 職責:初始化驅動自身的業務.Release = SampleDriverRelease, // 職責:釋放驅動資源,發生異常時也會調用
};
HDF_INIT (g_sampleDriverEntry) ;
宏展開后,在宏里定義了一個驅動的模塊名+HdfEntry的符號并放到.hdf.section,即在編譯時通過Scatter文件把HDF_INIT為開頭的模塊module的指針全部放到.hdf.section,放的Driver Entry的指針。
#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 宏是定義了一個“驅動模塊名+HdfEntry”的符號放到".hdf.driver"所在 section,該符號指向的內存地址即為驅動程序入口結構體的地址。這個特殊的 section 將用于開機啟動時查找設備驅動。
2.3、獲取驅動列表
HDF 驅動框架通過將驅動程序入口符號的地址集中存放到一個特殊的 section 來實現對驅動的索引,這個 section 的開頭和末尾插入了_hdf_drivers_start、_hdf_drivers_end 兩個特殊符號,用于標記這個 section 的范圍,兩個特殊符號之間的數據即為驅動實現指針。
2.4、獲取設備列表
系統啟動時會根據Device Info創建對應的Host 節點。配置文本編譯后會變成二進制格式的配置文件,其中設備相關信息被存放在一個用“hdf_manager”標記的 device_info 配置塊中,host的內容以塊的形式在 device_info 塊中依次排列,host 塊中記錄了 host 名稱、啟動優先級和設備列表信息。設備信息中的 moduleName 字段將用于和驅動程序入口中的 moduleName 進行匹配,從而為設備匹配到正確的驅動程序。從而為設備匹配到正確的驅動程序,完成設備與驅動的匹配,具體流程圖如下:
2.5、設備與驅動的匹配
系統啟動時Device Manager和Device Host 即HDF框架首先啟動,在系統啟動時,驅動框架先啟動(如何先啟動?),通過解析配置文件獲取到設備列表,通過讀取”.hdf
drivers"段讀取到驅動程序(Driver Entry)列表,然后遍歷設備列表與驅動程序列表進行匹配,并加
載匹配成功的驅動。驅動框架有兩大核心管理者:
- DeviceManager 負責設備的管理,包括設備加載、卸載和查詢等設備相關功能。
- DeviceServiceManager 負責管理設備發布的接口服務,提供接口服務的發布、查詢等功能。
驅動加載主要由 DeviceManager 主導,首先 DeviceManager 要解析配置文件中的Host 列表,根據Host 列表中的信息來實例化對應的Host 對象。Host 解析配置文件獲取到關聯的設備列表,遍歷設備列表去獲取與之匹配的驅動程序名稱,然后基于驅動程序名稱遍歷前面提到的.hdf.driver section 獲得驅動程序地址。
2.6、HDF驅動框架的啟動
HDF驅動框架通過late_initcall 啟動
2.7、驅動靜態加載的全流程總結
- 在系統啟動時,DeviceManagerInit通過late_initcall先啟動。
- Device Manager 根據 Device Information 信息,解析配置文件中的Host 列表,根據 Host 歹
表中的信息來實例化對應的 Host 對象。 - Host遍歷設備列表去獲取與之匹配的驅動程序名稱,然后基于驅動程序名稱遍歷.hdf.driver sec
on 獲得驅動程序地址 - 設備與驅動匹配成功之后,獲取指定驅動的入口地址,加載對應的設備驅動程序。
- 調用指定驅動的 Bind 接口,用于關聯設備和服務實例。
- 調用指定驅動的 Init 接口,用于完成驅動的相關初始化工作。
- 如果驅動被卸載或者因為硬件等原因Init 接口返回失敗,Release 將被調用,用于釋放驅動申請
的各類資源。
二、驅動框架交互流程
OpenHarmony 系統 HDF 驅動框架主要由驅動基礎框架、驅動程序、驅動配置文件和驅動接口這四個部分組成。
- HDF 驅動基礎框架提供統一的硬件資源管理,驅動加載管理以及設備節點管理等功能。驅動框架采用的是主從模式設計,由 Device Manager 和 Device Host 組成。Device Manager 提供了統一的驅動管理,Device Manager 啟動時根據 Device Information 提供驅動設備信息加載相應的驅動 Device Host,并控制 Host 完成驅動的加載。Device Host 提供驅動運行的環境,同時預置 Host Framework 與 Device Manager 進行協同,完成驅動加載和調用。根據業務的需求 Device Host 可以有多個實例。
- 驅動程序實現驅動具體的功能,每個驅動由一個或者多個驅動程序組成,每個驅動程序都對應著一個 Driver Entry。Driver Entry 主要完成驅動的初始化和驅動接口綁定功能。
- 驅動配置文件.hcs 主要由設備信息(Device Information)和設備資源(Device Resource)組成。Device Information 完成設備信息的配置。如配置接口發布策略,驅動加載的方式等。Device Resource 完成設備資源的配置。如 GPIO 管腳、寄存器等資源信息的配置。
- 驅動接口 HDI(Hardware Driver interface )提供標準化的接口定義和實現,驅動框架提供 IO Service和IO Dispatcher 機制,使得不同部署形態下驅動接口趨于形式一致。
驅動框架完成大部分驅動加載的動作,用戶只需注冊自己所需的接口和配置,然后驅動框架就會解析配置的內容,完成驅動加載和初始化動作。
三、驅動的開發
基于HDF(Hardware Driver Foundation開發驅動,開發者只需注冊所需接口和配置,驅動框架就會解析配置內容,完成驅動加載和初始化動作:
3.1、實現驅動的業務功能邏輯的三個接口
HDF_INIT(g_sampleDriverEntry); 系統內核自動調用的一個宏,驅動入口對象注冊到HDF框架,會調用HDF_INIT函數將驅動入口地址注冊到HDF框架。
當調用HDF_INIT將驅動入口注冊到HDF框架中,在加載驅動時HDF框架會先調用Bind函數,再調用Init函數加載該驅動,當Init調用異常時,HDF框架會調用Release釋放驅動資源并退出。所以首先就是驅動入口部分相關的Bind, Init 和Release三個接口:
- Bind接口,驅動對外提供的服務能力,將相關的服務接口綁定到HDF框架
該接口的作用主要是完成驅動設備和設備服務接口的bind動作。
int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject)
{return HDF_SUCCESS;
}
- Init接口
在HdfDriverEntry的Init方法中,應當調用HdfDeviceSetClass接口,對驅動的類型進行定義。驅動的類型可以用于歸類當前設備的驅動程序,也可以用來查詢當前設備的驅動能力。為了便于后續驅動的統一管理,建議通過HdfDeviceSetClass接口來設置當前驅動的類型。
int32_t SampleDriverInit(struct HdfDeviceObject *deviceObject)
{// 設置驅動的類型為DISPLAYif (!HdfDeviceSetClass(deviceObject, DEVICE_CLASS_DISPLAY)) {HDF_LOGE("HdfDeviceSetClass failed");return HDF_FAILURE;}return HDF_SUCCESS;
}
- Release接口
當用戶需要卸載驅動時,驅動框架先通過該接口通知驅動程序釋放資源,然后再釋放其他內部資源
void SampleDriverRelease(struct HdfDeviceObject *deviceObject)
{//驅動資源釋放的接口 Release all resources.return;
}
3.2、定義用于注冊驅動入口對象的結構體DriverEntry
定義驅動入口的對象,必須為HdfDriverEntry(在hdf_device_desc.h中定義)類型的全局變量,主要是把三個接口賦值,相當于是完成映射。
struct HdfDriverEntry g_deviceSample = {.moduleVersion = 1,.moduleName = "sample_driver", .Bind = SampleDriverBind,.Init = SampleDriverInit,.Release = SampleDriverRelease,
};
3.3、驅動配置信息
驅動配置包含兩部分,HDF框架定義的驅動設備描述和驅動的私有配置信息。HDF使用HCS作為配置描述源碼,內容以 Key-Value 鍵值對為主要形式。它實現了配置代碼與驅動代碼解耦,便于開發者進行配置管理。HC-GEN (全稱 HDF Configuration Generator) 是 HCS 配置轉換工具,可以將 HDF 配置文件轉換為軟件可讀取的文件格式:在弱性能環境中,轉換為配置樹源碼,驅動可直接調用 C代碼獲取配置;在高性能環境中,轉換為 HCB(HDF Configuration Binary)二進制文件,驅動可使用 HDF框架提供的配置解析接口獲取配置。HCS經過HC-GEN編譯生成HCB文件,HDF驅動框架中的HCS Parser模塊會從HCB文件中重建配置樹,HDF驅動模塊使用HCS Parser提供的配置讀取接口獲取配置內容。
3.3.1、驅動設備描述(必選)
HDF框架加載驅動所需要的信息來源于HDF框架定義的驅動設備描述,因此基于HDF框架開發的驅動必須要在HDF框架定義的device_info.hcs配置文件中添加對應的設備描述,驅動的設備描述填寫如下所示:
sample_host :: host{hostName = "host0"; //host名稱,host節點是用來存放某一類驅動的容器。priority = 100; //host啟動優先級(0-200),值越大優先級越低,建議默認配100,優先級相同則不保證host的加載順序。device_sample :: device { //sample設備節點。device0 :: deviceNode { //sample驅動的DeviceNode節點。policy = 1; //驅動服務發布的策略priority = 100; //驅動啟動優先級(0-200),值越大優先級越低,建議默認配 100,優先級相同則不保證 device 的加載順序preload = 0; //驅動按需加載字段permission = 0664;//驅動創建設備節點權限moduleName = "sample_driver"; //驅動名稱,該字段的值必須和驅動入口結構的moduleName值一致serviceName = "sample_service"; //驅動對外發布服務的名稱,必須唯一deviceMatchAttr = "sample_config";//驅動私有數據匹配的關鍵字,必須和驅動私有數據配置表中的match_attr值相等。}}
}
3.3.2、驅動私有配置信息(可選)
如果驅動有私有配置,則可以添加一個驅動的配置文件,用來填寫一些驅動的默認配置信息,HDF框架在加載驅動的時候,會將對應的配置信息獲取并保存在HdfDeviceObject 中的property里面,通過Bind和Init傳遞給驅動,驅動的配置信息示例如下:
root { SampleDriverConfig {sample_version = 1; sample_bus = "I2C_0"; match_attr = "sample_config"; //該字段的值必須和device_info.hcs中的deviceMatchAttr值一致}
}
3.3.3、配置SeLinux
在4.0后必須配置SeLinux 否則即使加載了So也無法正常被拉起,具體配置規則根據業務自行配置。
未完待續…