The HDF driver framework is the foundation of OpenAtom OpenHarmony (“OpenHarmony”) system hardware ecosystem openness. It provides driver framework capabilities such as driver loading, driver service management, and driver message mechanism for driver developers. It also provides developers with standardized HDI The hardware device interface allows developers to shield the southbound device differences and provide better hardware. This article will bring you an introduction to the HDI hardware device interface.
1. Introduction to HDI
HDI (Hardware Device Interface, hardware device interface) is a hardware standardized descriptive interface provided by the HDF driver framework for developers. In the OpenHarmony hierarchy, HDI sits between the "Basic System Services Layer" and the "Device Abstraction Layer (DAL)". The hardware device is abstracted through DAL and described based on the IDL (Interface Description Language) interface description language, which provides a standardized hardware device interface for upper-layer applications or services.
HDI supports two calling methods, "IPC mode" and "pass-through mode". Among them, the IPC mode is the cross-process communication mode, which is implemented based on the binder mechanism. The calling end calls the HDI interface through the Proxy proxy library. It has good decoupling and security, and is the default deployment method of the standard system. The pass-through mode implements HDI as a shared library, and the calling end uses dlopen to load the HDI implementation library and directly call the HDI interface. It is the default deployment method for small systems, and is also suitable for standard system modules with special performance requirements.
The advantages of HDI hardware device interface can be summed up in one sentence: it provides a unified realization path for hardware access. The specific implementation of the hardware interface is shielded, and the architecture of the system software and hardware is decoupled. Let developers focus on the use of hardware interfaces, thereby simplifying the development process and improving development efficiency.
2. HDI implementation
Through the above introduction, I believe many small partners will have questions, how is the HDI interface implemented? Below we will introduce the HDI interface implementation based on C/S (Client-Server client and server) structure in IPC mode.
2.1 IDL interface description language For the convenience of understanding later, let's briefly understand the IDL interface description language.
IDL (Interface Description Language) is a kind of language used to describe the interface. It defines the programming interface recognized by the client and the server in a neutral way, and can realize the inter-process communication (IPC) between the two. Cross-process communication means that one process can access data in another process, or call methods in another process. The application interface provider (for calling) is usually called the server, and the caller is called the client.
IDL first decomposes the objects that need to be passed into basic types that the operating system can understand, and then compiles according to the interface declaration to generate the C/C++ code of IPC/RPC proxy (Proxy) and stub (Stub), so as to provide the caller with a consistent interface and calling method.
2.2 Implementation of HDI interface based on IDL language First, use IDL syntax to describe the HDI interface and save it as a .idl file, then write the compilation script BUILD.gn file of the .idl file, and finally compile the .idl file. Below we will demonstrate the implementation process of the HDI interface of the power subsystem.
(1) Write the .idl file using IDL syntax ● Define the power interface 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);
}
● If you need to call back from the server, you can define the callback interface class IPowerHdiCallback.idl
package ohos.hdi.power.v1_0;
[callback] interface IPowerHdiCallback {
OnSuspend();
OnWakeup();
}
● If a custom data type is used in the interface, define the custom type to 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) Write the compilation script BUILD.gn of the .idl file
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) Compile the .idl file. Use the compilation tool hdi-gen to compile the IDL file. The IDL file is converted into C/C++ language function interface declaration, client and server IPC related process code during the compilation process. Developers only need to generate The power.h function interface can implement specific service functions.
The generated code after compilation is in out/product/gen/drivers/interface/power, and the interface code is as follows:
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) Implement HDI interface ● Implement UHDF Driver, which is used to load the HDI implementation as an independent process, and publish device services based on the HDF driver framework.
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,
};
● Implement HDI interface
#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;
}
3. Use of HDI
Through the above introduction, I believe that everyone has a certain understanding of HDI. Next, we will introduce the use of HDI. In pass-through mode, the call to the HDI interface is a function call in the same process space, and the process is relatively straightforward. Here we focus on Explain the calling principle in IPC mode, and then show the calling of power subsystem HDI through CPP language.
3.1 Calling principle In IPC mode, when the system service calls the HDI interface, the function call is converted into an IPC request through the proxy library, and the parameters of the interface call are serialized; the IPC request is sent to the server through the IPC framework, and the request will be stubed The library first processes, and then deserializes the parameters of the interface call, and then converts them into a function call to the service implementation, thereby realizing the interface call process.
3.2 Use of CPP Language Based on the above, the HDI interface of the power supply subsystem has been compiled and generated. Let's take a look at how to use the CPP language to call the HDI interface.
(1) The client adds dependencies in 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) Call the HDI interface in the code that implements the power subsystem, the code is as follows:
#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接口注册电源事件回调
}
4. Conclusion
The above is the whole content of this article. Here we briefly introduce the implementation ideas and use of the HDI interface. For the vast number of southbound developers, we also provide detailed HDI interface implementation guidance in the community. You are welcome to participate in more discussions in the gitee community.
Community link: https://gitee.com/openharmony/drivers_interface
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。