HDF 驱动框架是 OpenAtom OpenHarmony(简称“OpenHarmony”)系统硬件生态开放的基础,为驱动开发者提供了驱动加载、驱动服务管理和驱动消息机制等驱动框架能力,同时还为开发者提供了规范的HDI硬件设备接口,让开发者能屏蔽南向设备差异,提供更好的硬件。本文将为大家带来 HDI 硬件设备接口的介绍。
图1 HDF驱动框架
一、HDI介绍HDI(Hardware Device Interface,硬件设备接口)是 HDF 驱动框架为开发者提供的硬件规范化描述性接口。在 OpenHarmony 分层结构中,HDI 位于 “基础系统服务层”和“设备抽象层(DAL)”之间。硬件设备通过 DAL 抽象化,并基于 IDL(Interface Description Language)接口描述语言描述后,为上层应用或服务提供了规范的硬件设备接口。
图2 OpenHarmony 分层结构
HDI 支持“IPC 模式”及“直通模式”两种调用方式。其中,IPC 模式即跨进程通信模式,基于 binder 机制实现,调用端通过 Proxy 代理库调用 HDI 接口,具备良好的解耦性和安全性,是标准系统的默认部署方式。直通模式,将 HDI 实现为共享库,调用端使用 dlopen 加载 HDI 实现库并直接调用 HDI 接口,是小型系统的默认部署方式,同时还适用于对性能有特殊需求的标准系统模块。
图3 两种调用方式
HDI 硬件设备接口的优点用一句话总结就是:为硬件的接入提供了统一的实现通路。屏蔽了硬件接口的具体实现,实现系统软件与硬件的架构解耦。让开发者专注于硬件接口的使用,从而简化开发过程,提升开发效率。
二、HDI实现通过上文的介绍,相信很多小伙伴会有疑问,HDI 接口是怎么实现的呢?下面我们将为你介绍 IPC 模式下基于 C/S(Client-Server 客户端与服务端)结构的 HDI 接口实现。
2.1 IDL接口描述语言
为方便后文的理解,我们先简单了解一下 IDL 接口描述语言。
IDL(Interface Description Language)是一类用来描述接口的语言,通过一种中立的方式来定义客户端与服务端均认可的编程接口,可以实现在二者间的跨进程通信(IPC)。跨进程通信意味着可以在一个进程访问另一个进程的数据,或调用另一个进程的方法。通常把应用接口提供方(供调用)称为服务端,调用方称为客户端。
IDL 先把需要传递的对象分解成操作系统能够理解的基本类型,然后根据接口声明编译,生成 IPC/RPC代理(Proxy)和桩(Stub)的 C/C++ 代码,从而为调用者提供一致的接口和调用方式。
图4 IDL IPC模式通信模型
2.2 基于IDL语言实现HDI接口
首先,使用 IDL 语法描述 HDI 接口并保存为.idl文件,然后编写 .idl 文件的编译脚本 BUILD.gn 文件,最后编译 .idl 文件即可。下面我们将为大家演示电源子系统的 HDI 接口的实现过程。
(1)使用IDL语法编写 .idl 文件
● 定义电源接口 IPowerInterface.idl
package ohos.hdi.power.v1_0;import ohos.hdi.power.v1_0.IPowerHdiCallback;import ohos.hdi.power.v1_0.PowerTypes;interface IPowerInterface { RegisterCallback([in] IPowerHdiCallback ipowerHdiCallback); StartSuspend(); StopSuspend(); ForceSuspend(); SuspendBlock([in] String name); SuspendUnblock([in] String name); PowerDump([out] String info);}
● 如果需要从服务端回调,可以定义 callback 接口类 IPowerHdiCallback.idl
package ohos.hdi.power.v1_0;[callback] interface IPowerHdiCallback { OnSuspend(); OnWakeup();}
● 如果 interface 中用到了自定义数据类型,将自定义类型定义到 powerTypes.idl
package ohos.hdi.power.v1_0;enum PowerHdfCmd { CMD_REGISTER_CALLBCK = 0, CMD_START_SUSPEND, CMD_STOP_SUSPEND, CMD_FORCE_SUSPEND, CMD_SUSPEND_BLOCK, CMD_SUSPEND_UNBLOCK, CMD_DUMP,};enum PowerHdfCallbackCmd { CMD_ON_SUSPEND = 0, CMD_ON_WAKEUP,};enum PowerHdfState { AWAKE = 0, INACTIVE, SLEEP,};
(2)编写 .idl 文件的编译脚本 BUILD.gn
import("//drivers/adapter/uhdf2/hdi.gni")if (defined(ohos_lite)) { group("libpower_proxy_1.0") { deps = [] public_configs = [] }} else { hdi("power") { module_name = "power_interface_service" sources = [ "IPowerHdiCallback.idl", "IPowerInterface.idl", "PowerTypes.idl", ] language = "cpp" subsystem_name = "hdf" part_name = "power_device_driver" }}
(3)编译 .idl文件
使用编译工具 hdi-gen 编译 IDL 文件,IDL 文件在编译过程中转换为 C/C++ 语言的函数接口声明、客户端与服务端 IPC 相关过程代码,开发者只需要基于生成的 power.h 函数接口实现具体服务功能即可。
编译后生成代码在 out/product/gen/drivers/interface/power 中,接口代码如下:
namespace OHOS {namespace HDI {namespace Power {namespace V1_0 {using namespace OHOS; enum { CMD_POWER_INTERFACE_REGISTER_CALLBACK, CMD_POWER_INTERFACE_START_SUSPEND, CMD_POWER_INTERFACE_STOP_SUSPEND, CMD_POWER_INTERFACE_FORCE_SUSPEND, CMD_POWER_INTERFACE_SUSPEND_BLOCK, CMD_POWER_INTERFACE_SUSPEND_UNBLOCK, CMD_POWER_INTERFACE_POWER_DUMP, CMD_POWER_INTERFACE_GET_VERSION,}; class IPowerInterface : public IRemoteBroker {public: DECLARE_INTERFACE_DESCRIPTOR(u"ohos.hdi.power.v1_0.IPowerInterface"); virtual ~IPowerInterface() = default; static sptr<IPowerInterface> Get(); static sptr<IPowerInterface> GetInstance(const std::string& serviceName); virtual int32_t RegisterCallback(const sptr<IPowerHdiCallback>& ipowerHdiCallback) = 0; virtual int32_t StartSuspend() = 0; virtual int32_t StopSuspend() = 0; virtual int32_t ForceSuspend() = 0; virtual int32_t SuspendBlock(const std::string& name) = 0; virtual int32_t SuspendUnblock(const std::string& name) = 0; virtual int32_t PowerDump(std::string& info) = 0; virtual int32_t GetVersion(uint32_t& majorVer, uint32_t& minorVer) = 0;};} // V1_0} // Power} // HDI} // OHOS
(4)实现HDI接口
● 实现 UHDF Driver,用于将 HDI 实现加载为独立进程,并基于 HDF 驱动框架发布设备服务。
static int32_t PowerInterfaceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, struct HdfSBuf *reply){ ...... return hdfPowerInterfaceHost->service->OnRemoteRequest(cmdId, *dataParcel, *replyParcel, option); // 将接口调用转发到stub实现}static int HdfPowerInterfaceDriverBind(struct HdfDeviceObject *deviceObject){ ...... hdfPowerInterfaceHost->ioservice.Dispatch = PowerInterfaceDriverDispatch; hdfPowerInterfaceHost->ioservice.Open = NULL; hdfPowerInterfaceHost->ioservice.Release = NULL; hdfPowerInterfaceHost->service = new PowerInterfaceImpl(); deviceObject->service = &hdfPowerInterfaceHost->ioservice; return HDF_SUCCESS;}......struct HdfDriverEntry g_powerinterfaceDriverEntry = { .moduleVersion = 1, .moduleName = "power_interface_service", .Bind = HdfPowerInterfaceDriverBind, .Init = HdfPowerInterfaceDriverInit, .Release = HdfPowerInterfaceDriverRelease,};
● 实现 HDI 接口
#include "v1_0/power_interface_stub.h"/* 继承PowerInterfaceStub并实现IPowerInterface 中的接口*/class PowerInterfaceImpl : public PowerInterfaceStub {public: virtual ~PowerInterfaceImpl() {} int32_t RegisterCallback(const sptr<IPowerHdiCallback>& ipowerHdiCallback) override; int32_t StartSuspend() override; int32_t StopSuspend() override; int32_t ForceSuspend() override; int32_t SuspendBlock(const std::string& name) override; int32_t SuspendUnblock(const std::string& name) override; int32_t PowerDump(std::string& info) override;};// 在cpp中对相关接口进行实现,其中调用了内核相关接口,实现了具体功能int32_t PowerInterfaceImpl::StopSuspend(){ suspendRetry_ = false; return HDF_SUCCESS;}int32_t PowerInterfaceImpl::ForceSuspend(){ suspendRetry_ = false; NotifyCallback(CMD_ON_SUSPEND); DoSuspend(); NotifyCallback(CMD_ON_WAKEUP); return HDF_SUCCESS;}int32_t PowerInterfaceImpl::SuspendBlock(const std::string& name){ std::lock_guard<std::mutex> lock(mutex_); if (name.empty()) { return HDF_ERR_INVALID_PARAM; } UniqueFd fd(TEMP_FAILURE_RETRY(open(LOCK_PATH, O_RDWR | O_CLOEXEC))); bool ret = SaveStringToFd(fd, name); if (!ret) { return HDF_FAILURE; } return HDF_SUCCESS;}
三、HDI使用通过上文的介绍,相信大家已经对 HDI 有了一定的了解,下面我们将为大家介绍 HDI 的使用,在直通模式下,对 HDI 接口调用为同一进程空间函数调用,过程较为直接,这里我们重点阐述 IPC 模式下的调用原理,然后通过 CPP 语言来展示电源子系统 HDI 的调用。
3.1 调用原理
在 IPC 模式下,当系统服务调用 HDI 接口时,通过 proxy 库将函数调用转换为 IPC 请求,将接口调用的参数进行序列化;IPC 请求通过 IPC 框架发送到服务端,请求将被 stub 库先处理,然后对接口调用的参数进行反序列化,再转换成对服务实现的函数调用,从而实现接口调用过程。
图5 HDI调用过程
3.2 基于CPP语言的使用
上文已经编译生成了电源子系统的 HDI 接口,下面我们来看看如何使用 CPP 语言来调用 HDI 接口吧。
(1)客户端在BUILD.gn中增加依赖://drivers/interface/foo/v1.0:libfoo_proxy_1.0"
ohos_executable("call_foo_hdi") {sources = ["src/call_foo_hdi.cpp",]deps = ["//drivers/interface/foo/v1.0:libfoo_proxy_1.0",]external_deps = ["hiviewdfx_hilog_native:libhilog","ipc:ipc_core","utils_base:utils",]part_name = "bar"subsystem_name = "bar_subsystem"}
(2)在实现电源子系统的代码中调用 HDI 接口,代码如下:
#include "v1_0/power_interface.h" //包含Power HDI接口头文件 using namespace OHOS::HDI::Power::V1_0; //使用HDI接口命名空间 namespace OHOS { namespace PowerMgr { sptr<IPowerInterface> powerInterface = nullptr; SystemSuspendController::SystemSuspendController() { sptr<IPowerHdiCallback> g_callback = new PowerHdiCallbackImpl(); powerInterface = IPowerInterface::Get(); //调用接口实例化接口获取客户端实例 if (powerInterface == nullptr) { POWER_HILOGE(COMP_SVC, "The hdf interface is null"); return; } powerInterface->RegisterCallback(g_callback); // 调用HDI接口注册电源事件回调}
四、结语以上就是本文全部内容,我们在这里简单介绍了HDI接口的实现思路及使用,对于广大南向开发者,我们还在社区提供了详细的HDI接口实现指导,欢迎大家在gitee社区参与更多讨论。
社区链接:
https://gitee.com/openharmony/drivers_interface
|