OpenHarmony HDF HDI基礎能力分析與使用
HDI接口概述
回顧之前的文章,HDF 驅動框架的一個重要功能是為系統提供穩定的統一的硬件接口,這樣才能保證系統服務可以運行在不同硬件上而不需要額外的適配工作,HDI(Hardware Device Interfaces)正是為了實現該目的而設計。

HDI 是對硬件功能的較高層次抽象接口,各類外設完成 HDI 接口定義后便只會在 HDI 的兼容性規則下進行變更,從而保證接口的穩定性。具體的驅動實現不需要再重復定義 HDI 接口,只需要按需實現即可接入系統功能。
在不同量級的 OpenHarmony 系統上,HDI 存在兩種部署形態,IPC 模式和直通模式。

在輕量級 OpenHarmony 系統上,出于減小系統性能負載考慮,HDI 實現為用戶態共享庫,由系統服務直接加載 HDI 實現到自己進程中函數調用使用。HDI 實現封裝具體的用戶態內核態交互過程,當需要訪問驅動程序時使用 IO Service 請求將消息通過 system call 方式調用到內核驅動實現。
在標準 OpenHarmony 系統上,HDI 以獨立服務進程方式部署,系統服務只加載 HDI 客戶端實現到自己進程中,實際業務運行在獨立進程中,客戶端通過 IPC 與服務端交互,便于架構解耦、權限管理。
HDI接口實現
直通模式為函數實現方式,無論調用還是實現都不需要其他組件支持即可實現,這里將重點分析 IPC 模式的實現。
HDI發布

HDI IPC 模式基于 OpenHarmony 系統通信框架的通用模型,但是因為驅動很多時候涉及到底層操作和多系統遷移的場景而使用C語言編寫,所以驅動框架還提供了 HDI 服務的 C 語言實現的基礎組件,C++ 實現則主要使用系統通信框架組件。
HDI 服務發布基于 UHDF(用戶態 HDF 驅動框架)實現,通用的服務發布實現如下。
1. 實現驅動入口
- int SampleDriverBind(struct HdfDeviceObject *deviceObject)
- {
- HDF_LOGE("SampleDriverBind enter!");
- static struct IDeviceIoService testService = {
- .Dispatch = SampleServiceDispatch, // 服務回調接口
- };
- deviceObject->service = &testService;
- return HDF_SUCCESS;
- }
- int SampleDriverInit(struct HdfDeviceObject *deviceObject)
- {
- HDF_LOGE("SampleDriverInit enter");
- return HDF_SUCCESS;
- }
- void SampleDriverRelease(struct HdfDeviceObject *deviceObject)
- {
- HDF_LOGE("SampleDriverRelease enter");
- return;
- }
- struct HdfDriverEntry g_sampleDriverEntry = {
- .moduleVersion = 1,
- .moduleName = "sample_driver",
- .Bind = SampleDriverBind,
- .Init = SampleDriverInit,
- .Release = SampleDriverRelease,
- };
- HDF_INIT(g_sampleDriverEntry);
首先要添加一個 UHDF 驅動用于發布 IoService 服務,IoService 設備服務即為 HDI 服務實體。實現方式與 KHDF 驅動一致。
2. 實現服務響應接口
- int32_t SampleServiceOnRemoteRequest(struct HdfDeviceIoClient *client, int cmdId,
- struct HdfSBuf *data, struct HdfSBuf *reply)
- {
- switch (cmdId) {
- case SAMPLE_SERVICE_PING:
- return SampleServiceStubPing(client, data, reply);
- … …
- default:
- HDF_LOGE("SampleServiceDispatch: not support cmd %d", cmdId);
- return HDF_ERR_INVALID_PARAM;
- }
- }
- static int32_t SampleServiceDispatch(struct HdfDeviceIoClient *client, int cmdId,
- struct HdfSBuf *data, struct HdfSBuf *reply)
- {
- return SampleServiceOnRemoteRequest(client, cmdId, data, reply);
- }
當收到 HDI 調用時,服務響應接口"SampleServiceDispatch"將會被調用。
- client 調用者對象,在用戶態驅動中暫時未支持
- cmdId 調用命令字,用于區分調用的 API
- data 調用入參序列化對象,在 IPC 調用場景為 parcel 對象的 C 語言封裝,入參需要使用序列化接口從 data 對象中獲取后再使用
- reply 調用出參對象,需要返回給調用的信息寫入該序列化對象
如果 C++ 實現客戶端可以使用下面接口將 sbuf 對象轉換為 parcel 對象后操作:
- int32_t SbufToParcel(struct HdfSBuf *sbuf, OHOS::MessageParcel **parcel);
3. UHDF 驅動配置
- platform :: host {
- hostName = "sample_host";
- priority = 50;
- sample_device :: device {
- device0 :: deviceNode {
- policy = 2;
- priority = 100;
- moduleName = "libsample_driver.z.so";
- serviceName = "sample_driver_service";
- }
- }
- }
參數說明:
- host 一個 host 節點即為一個獨立進程,如果需要獨立進程,新增屬于自己的 host 節點
- policy 服務發布策略,HDI 服務設置為 2
- moduleName 驅動實現庫名
- serviceName 服務名稱,請保持全局唯一性
因為 HDI 服務 C 和 C++ 實現使用的 IPC 組件不一樣,面向對象實現也不一致,所以在具體實現上存在一些差異。
HDI基礎組件
UHDF 框架為了支持 HDI 實現,提供了以下基礎組件(僅用于 C 語言 HDI 實現):
SBuf
SBuf 是同時支持 KHDF 和 UHDF 驅動 IoService 消息序列化的工具對象。在 UHDF IPC 通信場景中,SBuf 可以與系統 IPC 框架序列化對象 MessageParcel 對象(僅支持 C++ )相互轉換,從而實現 C 和 C++ 實現的 IPC 互通。
常用 API 如下:
- struct HdfSBuf;
- struct HdfSbufImpl;
- struct HdfRemoteService;
- /**
- * @brief HdfSBuf類型定義。
- *
- * @since 1.0
- */
- enum HdfSbufType {
- SBUF_RAW = 0, /* 用于用戶態內核態通信的sbuf類型 */
- SBUF_IPC, /* 用于跨進程通信的sbuf類型 */
- SBUF_IPC_HW, /* 用于擴展的預留類型 */
- SBUF_TYPE_MAX, /* sbuf類型最大值 */
- };

上述接口均有對應的寫入接口,不再一一列舉,可查閱官網API參考文檔。
RemoteService
RemoteService 對象和系統 IPC 框架中的 IRemoteObject 對象(僅支持 C++)對應并可以相互轉換,表示一個 IPC 對象。相關 API 說明:
- // 消息分發器,用于服務端響應調用或者在客戶端發起調用
- struct HdfRemoteDispatcher {
- int (*Dispatch)(struct HdfRemoteService *, int, struct HdfSBuf *, struct HdfSBuf *);
- };
- // RemoteService 死亡回調對象
- struct HdfDeathRecipient {
- void (*OnRemoteDied)(struct HdfDeathRecipient *, struct HdfRemoteService *);
- };
- struct HdfRemoteService {
- struct HdfObject object_;
- struct HdfObject *target;
- struct HdfRemoteDispatcher *dispatcher;
- bool isHw;
- };
- // 以自定義的消息分發器實例化一個RemoteService
- struct HdfRemoteService *HdfRemoteServiceObtain(
- struct HdfObject *object, struct HdfRemoteDispatcher *dispatcher);
- // 回收RemoteService對象
- void HdfRemoteServiceRecycle(struct HdfRemoteService *service);
- // 添加RemoteService的死亡通知,如果對應RemoteService的進程異常退出,HdfDeathRecipient的回調接口將被調用
- void HdfRemoteServiceAddDeathRecipient(struct HdfRemoteService *service, struct HdfDeathRecipient *recipient);
基于 RemoteService 實現一個服務端的示例:
- int SampleServiceStubDispatch(
- struct HdfRemoteService* service, int code, struct HdfSBuf *data, struct HdfSBuf *reply)
- {
- // IPC 調用響應接口
- int ret = HDF_FAILURE;
- switch (code) {
- case SAMPLE_IF_0: {
- // do something
- break;
- }
- default: {
- ret = HDF_ERR_INVALID_PARAM;
- }
- }
- return ret;
- }
- bool SampleStubConstruct()
- {
- // 構造消息分發器,實現消息處理回調
- static struct HdfRemoteDispatcher dispatcher = {
- .Dispatch = SampleServiceStubDispatch
- };
- // 實例化RemoteService
- inst->remote = HdfRemoteServiceObtain((struct HdfObject *)inst, &dispatcher);
- if (inst->remote == NULL) {
- HDF_LOGE("Device service manager failed to obtain remote service");
- return false;
- }
- … …
直接基于 RemoteService 實現服務端只適用于需要實現匿名 IPC 服務的情況,基于 UHDF 發布 HDI 服務只需要實現 Driver 綁定的 IoService 即可。
RemoteService 客戶端對象只能從 SBuf HdfSBufReadRemoteService 接口獲取。
HDI實現

- Driver 為 HDI 服務的驅動入口實現
- IoService 為 HDI 服務的服務入口實現,IoService 的 Dispatch 方法中調用 ServiceStub 中的真正服務響應接口(OnRemoteRequest)
- ServiceStub 為服務端實現對象,主要處理與 IPC 相關的業務邏輯,在這里完成參數反序列化后調用真正的 Service 實現接口,即 ServiceImpl 接口
- ServiceImpl 為 HDI 接口的真正實現,這里不關注 IPC 過程,只實現函數接口。
- 驅動框架提供了實現的樣例代碼,可參考 gitee driver 代碼倉。
HDI接口調用
HDI驅動框架HDI接口
HDI 服務管理功能由驅動框架 DeviceManager 實現,所以驅動框架提供了 HDI 服務管理相關 HDI 接口。
C++實現:
- namespace OHOS {
- namespace HDI {
- namespace ServiceManager {
- namespace V1_0 {
- struct IServiceManager : public IRemoteBroker {
- public:
- DECLARE_INTERFACE_DESCRIPTOR(u"HDI.IServiceManager.V1_0");
- // get()靜態方法用于獲取IServiceManager對象實例
- static ::OHOS::sptr<IServiceManager> Get();
- // GetService()接口是真正提供的HDI接口,用于查詢并獲取其他HDI服務的客戶端對象
- virtual ::OHOS::sptr<IRemoteObject> GetService(const char* serviceName) = 0;
- };
- } // namespace V1_0
- } // namespace ServiceManager
- } // namespace HDI
- } // namespace OHOS
C 實現:
- #ifdef __cplusplus
- extern "C" {
- #endif /* __cplusplus */
- struct HDIServiceManager {
- struct HdfRemoteService *remote;
- struct HdfRemoteService *(*GetService)(struct HDIServiceManager *self, const char* serviceName);
- };
- struct HDIServiceManager *HDIServiceManagerGet(void);
- void HDIServiceManagerRelease(struct HDIServiceManager *servmgr);
- #ifdef __cplusplus
- }
- #endif /* __cplusplus */
C 語言因為缺少原生的面向對象支持,這里我們采用 OOC 的實現,函數方法 HDIServiceManagerGet/Release 用于 HDIServiceManager 對象的實例化和釋放,HDI 接口關聯在接口對象內部成員中,與 C++實現類似。
HDI客戶端實現

HDI 客戶端同時支持 C 和 C++ 實現,實現方法較為簡單,只需 realize HDI 接口類即可。提供 C++ 實現基于系統 IPC 子系統的統一模型,C 語言基于 RemoteService 和 SBuf 組件實現,但是有一些公共的約定:
客戶端提供接口對象,接口與對象綁定且必須與 HDI 一致
提供服務接口對象的實例化和釋放接口。
客戶端實現 IPC 過程,只為調用者暴露函數化接口。
HDI接口調用
HDI 客戶端接口已經提供了服務獲取接口,調用者調用服務獲取接口后再調用服務對象方法即可完成 HDI 調用。
這里以服務管理 HDI 接口為例:
C++接口調用:
- #include <iservmgr_hdi.h>
- void GetTestService()
- {
- auto servmgr = IServiceManager::Get();
- if (servmgr == nullptr) {
- HDF_LOGE("failed to get IServiceManager");
- return;
- }
- auto sampleService = servmgr->GetService(TEST_SERVICE_NAME);
- if (sampleService == nullptr) {
- HDF_LOGE("failed to get TEST_SERVICE");
- return;
- }
- // do something
- }
C 接口調用:
- #include <servmgr_hdi.h>
- void GetTestService()
- {
- struct HDIServiceManager *servmgr = HDIServiceManagerGet();
- if (servmgr == nullptr) {
- HDF_LOGE("failed to get IServiceManager");
- return;
- }
- struct HdfRemoteService *sampleService = servmgr->GetService(servmgr, TEST_SERVICE_NAME);
- if (sampleService == nullptr) {
- HDF_LOGE("failed to get TEST_SERVICE");
- return;
- }
- // do something
- }
總結
本文介紹了 HDI 的總體方案,重點介紹了 HDI 的 IPC 模式具體實現方法和驅動框架能力,相信對讀者理解和使用 HDI 有所幫助。