同步操作将从 yue/hdi_develop_guide 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
HDI(Hardware Device Interface)为系统提供统一稳定的硬件接口,是对硬件功能的较高层次的抽象接口,各类外设完成HDI接口定义后便只会在HDI的兼容性规则下进行变更,从而保证接口的稳定性。具体的驱动实现不需要再重复定义HDI接口,只需要按需实现即可接入系统功能。
在不同量级的OpenHarmony系统上,HDI存在两种部署形态,IPC模式和直通模式。
本文档以C++侧用户态驱动IPC模式开发实例展示HDI开发流程。
HDI系统部件开发旨在将idl文件编译生成出HDI系统接口so文件和提供给服务使用的so文件。
在drivers_interface仓下添加对应模块的idl文件。
master$ tree ./drivers/interface/foo
./drivers/interface/foo
├── bundle.json
└── v1_0
├── BUILD.gn # 编译idl文件的BUILD.gn
├── IBar.idl # 定义普通接口
├── IFoo.idl # 定义驱动接口
├── IFooCallback.idl # 定义用于回调的接口
└── Types.idl # 定义自定义类型数据
IFoo.idl
package ohos.hdi.foo.v1_0; // 包名
import ohos.hdi.foo.v1_0.IFooCallback; // 导入IFooCallback.idl文件
import ohos.hdi.foo.v1_0.IBar; // 导入IBar.idl文件
import ohos.hdi.foo.v1_0.Types; // 导入Types.idl文件
// 接口定义 接口名为'IFoo',必须与文件名保持一致
interface IFoo {
[oneway] Ping([in] String msg);
InfoTest([in] struct Info inParam, [out] struct Info outParam);
SendCallback([in] IFooCallback cbObj);
GetServObj([out] IBar servObj);
}
IBar.idl
package ohos.hdi.foo.v1_0;
interface IBar {
Echo([in] String sendMsg, [out] String recvMsg);
}
IFooCallback.idl
package ohos.hdi.foo.v1_0;
[callback] interface IFooCallback {
[oneway] Notify([in] String message);
}
Types.idl
package ohos.hdi.foo.v1_0;
// 枚举类型
enum InfoType {
FOO_TYPE_ONE = 1,
FOO_TYPE_TWO = 2,
};
// 结构体类型
struct Info {
unsigned int id;
String name;
enum InfoType type;
};
// 联合体类型
union UInfo {
byte m1;
int m2;
};
同级目录下创建BUILD.gn文件
import("//drivers/hdf_core/adapter/uhdf2/hdi.gni") # 编译idl必须要导入的模板
hdi("foo") { # 目标名称,会生成两个so: libfoo_[proxy/stub]_[major_ver].[minor_ver].z.so
module_name = "foo" # module_name匹配dirver文件中驱动描述符(HdfDriverEntry)的moduleName
sources = [ # 参与编译的idl文件
"IFoo.idl",
"IBar.idl",
"IFooCallback.idl",
"MyTypes.idl",
]
language = "cpp" # 控制idl生成c或c++代码 可选择`c`或`cpp`
mode = "ipc" # 指定为ipc模式,亦可指定为"passthrough"表示直通模式,不添加此选项,默认为ipc
subsystem_name = "hdf" # 子系统,统一填写“hdf”
part_name = "drivers_interface_foo" # 部件名,如果不属于已有部件,则需要定义新的部件
}
部件化bundle.json的基本参考信息
{
"component": {
"name": "部件名",
"subsystem": "子系统名",
"features":[不同形态下的特定功能],
"build": {
"sub_component": [
"模块:目标"
],
"test": [包含的测试用例集合],
"inner_kits": [
{
"name": "模块:目标名",
"header": {
"header_files": [对外暴露的头文件],
"header_base": "对外暴露头文件的文件夹路径"
}
},
]
}
}
}
常见配置项:
这里给出bundle.json模板进行参考,添加部件配置://drivers/interface/foo/bundle.json
{
"name": "drivers_interface_foo",
"description": "foo device driver interface",
"version": "3.2",
"license": "Apache License 2.0",
"component": {
"name": "drivers_interface_foo",
"subsystem": "hdf",
"syscap": [""],
"adapter_system_type": ["standard"],
"rom": "675KB",
"ram": "1024KB",
"deps": {
"components": [
"ipc",
"hdf_core",
"hiviewdfx_hilog_native",
"utils_base"
],
"third_part": [
"bounds_checking_function"
]
},
"build": {
"sub_component": [
"//drivers/interface/foo/v1_0:foo_idl_target"
],
"test": [
],
"inner_kits": [
{
"name": "//drivers/interface/foo/v1_0:libfoo_proxy_1.0",
"header": {
"header_files": [
],
"header_base": "//drivers/interface/foo"
}
},
{
"name": "//drivers/interface/foo/v1_0:foo_idl_headers",
"header": {
"header_files": [
],
"header_base": "//drivers/interface/foo"
}
}
]
}
}
}
其中:
以master分支,rk3568产品为例,在此文件中添加部件配置:
//productdefine/common/inherit/rich.json
{
"component": "drivers_interface_foo",
"features": []
}
其他产品配置文件如下:
分支 | 产品 | 编译入口配置文件 |
---|---|---|
master | rk3568 | //productdefine/common/inherit/rich.json |
master | Hi3516DV300 | //vendor/hisilicon/hispark_taurus_standard/config.json |
以上配置完成后,即可指定部件名进行编译,以master分支,编译rk3568为例:
./build.sh --product-name rk3568 --ccache --build-target drivers_interface_foo
编译成功后,可在out/rk3568/gen/drivers/interface/foo/v1_0目录下生成HDI源码:
master_9_7$ tree ./out/rk3568/gen/drivers/interface/foo/
./out/rk3568/gen/drivers/interface/foo/
└── v1_0
├── bar_proxy.cpp
├── bar_proxy.h
├── bar_service.cpp // IBar接口实现源文件,代码模板
├── bar_service.h // IBar接口实现头文件,代码模板
├── bar_stub.cpp
├── bar_stub.h
├── drivers_interface_foo__libfoo_proxy_1.0_external_deps_temp.json
├── drivers_interface_foo__libfoo_stub_1.0_external_deps_temp.json
├── foo_callback_proxy.cpp
├── foo_callback_proxy.h
├── foo_callback_service.cpp // IFooCallback接口实现源文件,代码模板
├── foo_callback_service.h // IFooCallback接口实现头文件,代码模板
├── foo_callback_stub.cpp
├── foo_callback_stub.h
├── foo_driver.cpp // 驱动加载入口代码模板,仅供参考
├── foo_proxy.cpp
├── foo_proxy.h
├── foo_service.cpp // IFoo接口实现源文件,代码模板
├── foo_service.h // IFoo接口实现头文件,代码模板
├── foo_stub.cpp
├── foo_stub.h
├── ibar.h // IBar对外接口头文件
├── ifoo.h // IFoo对外接口头文件
├── ifoo_callback.h // IFooCallback对外接口头文件
├── libfoo_proxy_1.0__notice.d
├── libfoo_stub_1.0__notice.d
├── types.cpp
└── types.h // 对外头文件
注意:
按需新增驱动模块目录,以下为参考:
//drivers/peripheral/foo
./peripheral/foo
├── BUILD.gn # 模块编译BUILD.gn
├── bundle.json # 部件化配置
├── hdi_service # hdi服务代码
│ ├── BUILD.gn # hdi服务代码编译BUILD.gn
│ ├── bar_impl.cpp # IBar接口实现源文件
│ ├── bar_impl.h # IBar接口实现头文件
│ ├── foo_driver.cpp # 驱动入口
│ ├── foo_impl.cpp # IFoo接口实现源文件
│ └── foo_impl.h # IFoo接口实现头文件
└── test # TDD测试用例
├── BUILD.gn # 测试用例代码编译BUILD.gn
├── foo_callback_impl.cpp # callback实现源文件
├── foo_callback_impl.h # callback实现头文件
└── foo_hdi_test.cpp # TDD测试用例代码
注意:
接口实现
//drivers/peripheral/foo/hdi_service/foo/foo_impl.h
#ifndef OHOS_HDI_FOO_V1_0_FOO_IMPL_H
#define OHOS_HDI_FOO_V1_0_FOO_IMPL_H
#include "v1_0/ifoo.h"
namespace OHOS {
namespace HDI {
namespace Foo {
namespace V1_0 {
class FooImpl : public IFoo {
public:
FooImpl();
virtual ~FooImpl() = default;
int32_t Ping(const std::string& msg) override;
int32_t InfoTest(const Info& inParam, Info& outParam) override;
int32_t SendCallback(const sptr<IFooCallback>& cbObj) override;
int32_t GetServObj(sptr<IBar>& servObj) override;
private:
sptr<IBar> bar_;
};
} // V1_0
} // Foo
} // HDI
} // OHOS
#endif // OHOS_HDI_FOO_V1_0_FOO_IMPL_H
//drivers/peripheral/foo/hdi_service/foo/foo_impl.cpp
#include "foo_impl.h"
#include <hdf_base.h>
#include <hdf_log.h>
#include "bar_impl.h"
namespace OHOS {
namespace HDI {
namespace Foo {
namespace V1_0 {
extern "C" IFoo *FooImplGetInstance(void)
{
return new (std::nothrow) FooImpl();
}
FooImpl::FooImpl() : bar_(new BarImpl())
{
}
int32_t FooImpl::Ping(const std::string& msg)
{
HDF_LOGI("%{public}s: msg:%{public}s", __func__, msg.c_str());
return HDF_SUCCESS;
}
int32_t FooImpl::InfoTest(const Info& inParam, Info& outParam)
{
HDF_LOGI("%{public}s:", __func__);
outParam = inParam;
return HDF_SUCCESS;
}
int32_t FooImpl::SendCallback(const sptr<IFooCallback>& cbObj)
{
HDF_LOGI("%{public}s:", __func__);
int32_t ret = cbObj->Notify("callback message");
if (ret != HDF_SUCCESS) {
HDF_LOGE("%{public}s: failed to notify callback", __func__);
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
int32_t FooImpl::GetServObj(sptr<IBar>& servObj)
{
HDF_LOGI("%{public}s:", __func__);
servObj = bar_;
return HDF_SUCCESS;
}
} // V1_0
} // Foo
} // HDI
} // OHOS
以上为示例,开发者按需填充业务代码。
开发者参考idl编译生成的foo_driver.cpp按需进行修改,并手动配置编译。
//drivers/peripheral/foo/hdi_service/BUILD.gn
# Copyright (C) 2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//build/ohos.gni")
import("//drivers/hdf_core/adapter/uhdf2/uhdf.gni")
ohos_shared_library("libfoo_service_1.0") {
sources = [
"foo_impl.cpp",
"bar_impl.cpp",
]
if (is_standard_system) {
external_deps = [
"c_utils:utils",
"drivers_interface_foo:foo_idl_headers",
"hiviewdfx_hilog_native:libhilog",
"ipc:ipc_single",
]
} else {
external_deps = [
"hilog:libhilog",
"ipc:ipc_single",
]
}
install_images = [ chipset_base_dir ]
subsystem_name = "hdf"
part_name = "drivers_peripheral_foo"
}
ohos_shared_library("libfoo_driver") {
sources = [ "foo_driver.cpp" ]
deps = [ "//drivers/interface/foo/v1_0:libfoo_stub_1.0" ]
if (is_standard_system) {
external_deps = [
"hdf_core:libhdf_host",
"hdf_core:libhdf_ipc_adapter",
"hdf_core:libhdf_utils",
"hdf_core:libhdi",
"hiviewdfx_hilog_native:libhilog",
"ipc:ipc_single",
"utils_base:utils",
]
} else {
external_deps = [
"hilog:libhilog",
"ipc:ipc_single",
]
}
install_images = [ chipset_base_dir ]
subsystem_name = "hdf"
part_name = "drivers_peripheral_foo"
}
group("hdi_foo_service") {
deps = [
":libfoo_service_1.0",
":libfoo_driver",
]
}
//drivers/peripheral/foo/BUILD.gn
if (defined(ohos_lite)) {
group("foo_entry") {
deps = [ ]
}
} else {
group("foo_entry") {
deps = [
"//drivers/peripheral/foo/hdi_service:hdi_foo_service"
]
}
}
{
"name": "drivers_peripheral_foo",
"description": "foo device driver",
"version": "3.1",
"license": "Apache License 2.0",
"component": {
"name": "drivers_peripheral_foo",
"subsystem": "hdf",
"syscap": [""],
"adapter_system_type": ["standard"],
"rom": "675KB",
"ram": "7400KB",
"deps": {
"components": [
"ipc",
"device_driver_framework",
"hiviewdfx_hilog_native",
"utils_base"
],
"third_part": [
"bounds_checking_function"
]
},
"build": {
"sub_component": [
"//drivers/peripheral/foo:foo_entry"
],
"test": [
"//drivers/peripheral/foo/test:foo_hdi_test"
],
"inner_kits": [
]
}
}
}
以master分支代码、rk3568产品为例://productdefine/common/inherit/chipset_common.json
{
"component": "drivers_peripheral_foo",
"features": []
}
其他分支配置文件:
分支 | 产品 | 编译入口配置文件 |
---|---|---|
master | rk3568 | //productdefine/common/inherit/chipset_common.json |
master | Hi3516DV300 | //vendor/hisilicon/hispark_taurus_standard/config.json |
与编译系统部件编译类似:
./build.sh --product-name rk3568 --ccache --build-target drivers_peripheral_foo
通过上面的步骤,HDI基础的so包括以下:
接口实现库主要为接口实现代码,如本例中的libfoo_service.z.so,为了使得IPC模式兼容直通模式,接口实现库名需按如下规则进行命名:
情况一:当使用IFoo::Get(bool isStub = false)函数时
foo_proxy.cpp
sptr<IFoo> IFoo::Get(bool isStub)
{
return IFoo::Get("foo_service", isStub); // 默认服务名称为"foo_service"
}
情况二:当使用Foo::Get(const std::string serviceName, bool isStub)时:
以master分支,rk3568产品为例,在vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs添加驱动服务配置
foo :: host {
hostName = "foo_host";
priority = 50;
uid = ""; // 用户态进程uid,缺省为空,会被配置为hostName的定义值,即普通用户
gid = ""; // 用户态进程gid,缺省为空,会被配置为hostName的定义值,即普通用户组
caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; // 用户态进程Linux capabilities配置,缺省为空,需要业务模块按照业务需要进行配置
foo_device :: device {
device0 :: deviceNode {
policy = 2;
priority = 100;
moduleName = "libfoo_driver.z.so";
serviceName = "foo_service";
}
}
}
默认示例配置,仅供参考:
foo :: host {
hostName = "foo_host";
priority = 50;
foo_device :: device {
device0 :: deviceNode {
policy = 2;
priority = 100;
moduleName = "libfoo_driver.z.so";
serviceName = "foo_service";
}
}
}
注意:
对于在hcs中新增加的host节点,需要新增配置对应进程的uid(用户ID)和gid(组ID)
passwd文件为系统用户配置文件,存储了系统中所有用户的基本信息,这里以此为例:
base/startup/init/services/etc/passwd
foo_host:x:1089:1089:::/bin/false
每行用户信息使用‘:’作为分隔符,划分为7哥字段,每个字段所表示的含义如下:
用户名:密码:UID(用户ID):GID(组ID):描述信息:主目录:默认shell
group为用户组配置文件,存储了所有用户组的信息,以下为例:
base/startup/init/services/etc/group
foo_host:x:1089:
每行代表一个用户组,用户组中以“:”作为分隔符,分为4个字段,每个字段的含义如下:
组名:密码:GID(组ID):该用户组中的用户列表
注意:
selinux目前只应用于开源分支,闭源分支无需配置
//base/security/selinux/sepolicy/ohos_policy/drivers/adapter/vendor/type.te
type foo_host, hdfdomain, domain;
注意:foo_host为device_info.hcs中的hostName
//base/security/selinux/sepolicy/ohos_policy/drivers/adapter/vendor/hdf_host.te
allow foo_host hdf_device_manager:hdf_devmgr_class { get };
allow foo_host hdf_foo_service:hdf_devmgr_class { add };
注意:foo_host为device_info.hcs中的hostName
//base/security/selinux/sepolicy/base/public/hdf_service.te
type hdf_foo_service, hdf_service_attr;
//base/security/selinux/sepolicy/base/public/hdf_service_contexts
foo_service u:object_r:hdf_foo_service:s0
c++侧接口
class IFoo : public HdiBase {
public:
DECLARE_HDI_DESCRIPTOR(u"ohos.hdi.foo.v1_0.IFoo");
virtual ~IFoo() = default;
// 获取默认服务名为"foo_service"的HDI服务,isStub = false表示IPC模式,true表示直通模式
static sptr<IFoo> Get(bool isStub = false);
// 功能同上,但可指定服务名
static sptr<IFoo> Get(const std::string &serviceName, bool isStub = false);
};
c侧接口
struct IFoo {
...
};
// 获取默认服务名为"foo_service"的HDI服务,isStub = false表示IPC模式,true表示直通模式
struct IFoo *IFooGet(bool isStub);
// 功能同上,但可指定服务名
struct IFooGetInstance(const char *serviceName, bool isStub);
// 释放HDI服务对象
void IFooRelease(struct IFoo *instance, bool isStub);
// 释放指定服务名对应的服务对象
void IFooReleaseInstance(const char *serviceName, struct IFoo *instance, bool isStub);
c++侧调用示例
#include <string>
#include "v1_0/ifoo.h" // 包含HDI接口头文件
using namespace OHOS::HDI::Foo::V1_0; // HDI命名空间
sptr<IFoo> client = IFoo::Get(); // 获取HDI对象,以ipc模式
int32_t ret = client->Ping("hello"); // 调用接口
c侧调用示例:
#include "ifoo.h"
struct IFoo *client = IFooGet(false); // ipc模式
int32_t ret = client->Ping("hello world");
IFooRelease(client, false);
callback提供自底向上的反向调用功能,开发者按需定义callback接口,在客户端代码中实现callback接口。
在本示例中,IFooCallback为callback接口类,则根据idl编译生成的源码提供的模板(foo_callback_service.h/.cpp),手动实现callback接口,如下:
//drivers/peripheral/foo/test/
$ tree ./peripheral/foo/test/
./peripheral/foo/test/
├── BUILD.gn
├── foo_callback_impl.cpp
├── foo_callback_impl.h
└── foo_hdi_test.cpp
foo_callback_impl.h
#ifndef OHOS_HDI_FOO_V1_0_FOOCALLBACK_IMPL_H
#define OHOS_HDI_FOO_V1_0_FOOCALLBACK_IMPL_H
#include "v1_0/ifoo_callback.h"
namespace OHOS {
namespace HDI {
namespace Foo {
namespace V1_0 {
class FooCallbackImpl : public IFooCallback {
public:
FooCallbackImpl() = default;
virtual ~FooCallbackImpl() = default;
int32_t Notify(const std::string& message) override;
};
} // V1_0
} // Foo
} // HDI
} // OHOS
#endif // OHOS_HDI_FOO_V1_0_FOOCALLBACK_IMPL_H
foo_callback_impl.cpp
#include "foo_callback_impl.h"
#include <hdf_base.h>
#include <hdf_log.h>
#define HDF_LOG_TAG foo_callback_service
namespace OHOS {
namespace HDI {
namespace Foo {
namespace V1_0 {
int32_t FooCallbackImpl::Notify(const std::string& message)
{
HDF_LOGI("%{public}s: recv message:%{public}s", __func__, message.c_str());
return HDF_SUCCESS;
}
} // V1_0
} // Foo
} // HDI
} // OHOS
客户端发送callback对象
sptr<IFooCallback> cbService = new FooCallbackImpl();
int32_t ret = client->SendCallback(cbService);
服务端获取并调用callback对象接口
int32_t FooImpl::SendCallback(const sptr<IFooCallback>& cbObj)
{
HDF_LOGI("%{public}s:", __func__);
int32_t ret = cbObj->Notify("callback message");
if (ret != HDF_SUCCESS) {
HDF_LOGE("%{public}s: failed to notify callback", __func__);
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
死亡监听用于在IPC模式下监听服务对象是否死亡,以下为使用示例:
C++侧使用示例:
#include <iproxy_broker.h>
#include <iremote_object.h>
using OHOS::sptr;
using OHOS::wptr;
using OHOS::IRemoteObject;
// 死亡监听类
class FooDeathRecipient : public IRemoteObject::DeathRecipient {
public:
void OnRemoteDied(const wptr<IRemoteObject> &object) override // 死亡监听回调,需重写
{
HDF_LOGE("%{public}s: foo service is dead", __func__);
}
};
int main() {
sptr<IFoo> fooService = IFoo::Get(false); // 获取HDI服务对象
const sptr<IRemoteObject::DeathRecipient> recipient = new FooDeathRecipient(); // 创建死亡监听对象
sptr<IRemoteObject> remote = OHOS::HDI::hdi_objcast<IFoo>(fooService); // 获取remote对象
bool ret = remote->AddDeathRecipient(recipient); // 注册死亡监听对象
// ...
ret = remote->RemoveDeathRecipient(recipient); // 注销死亡监听对象
return 0;
}
C侧使用示例:
// 死亡监听类
struct FooDeathRecipient {
struct HdfDeathRecipient recipient;
};
// 死亡监听回调
static void FooOnRemoteDied(struct HdfDeathRecipient *deathRecipient, struct HdfRemoteService *remote)
{
struct FooDeathRecipient *fooRecipient = CONTAINER_OF(deathRecipient, struct FooDeathRecipient, recipient);
}
int main() {
struct IFoo *fooService = IFooGet(false); // 获取HDI服务对象
struct FooDeathRecipient deathRecipient = { // 创建死亡监听对象
.recipient = {
.OnRemoteDied = FooOnRemoteDied, // 绑定回调函数
}
};
struct HdfRemoteService *remote = fooService->AsObject(fooService); // 获取remote对象
HdfRemoteAdapterAddDeathRecipient(remote, &deathRecipient.recipient); // 注册死亡监听
// ...
HdfRemoteAdapterRemoveDeathRecipient(remote, &deathRecipient.recipient); // 注销死亡监听
IFooRelease(fooService, false); // 释放HDI服务对象
return 0;
}
HDI提供服务监听能力,以下为示例:
服务端,驱动加载入口设置驱动类型:
static int HdfFooDriverInit(struct HdfDeviceObject *deviceObject)
{
// 设置驱动设备类型
if (!HdfDeviceSetClass(deviceObject, DEVICE_CLASS_DEFAULT)) {
HDF_LOGI("%{public}s: failed to set class of device object", __func__);
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
驱动设备类型定义于以下头文件中,
//drivers/hdf_core/framework/include/core/hdf_io_service_if.h
/**
* @brief Enumerates different classes of driver devices.
*
* @since 1.0
*/
typedef enum {
DEVICE_CLASS_DEFAULT = 0x1 << 0, /** Default device */
DEVICE_CLASS_PLAT = 0x1 << 1, /** Platform device */
DEVICE_CLASS_SENSOR = 0x1 << 2, /** Sensor device */
DEVICE_CLASS_INPUT = 0x1 << 3, /** Input device */
DEVICE_CLASS_DISPLAY = 0x1 << 4, /** Display device */
DEVICE_CLASS_AUDIO = 0x1 << 5, /** Audio device */
DEVICE_CLASS_CAMERA = 0x1 << 6, /** Camera device */
DEVICE_CLASS_USB = 0x1 << 7, /** USB device */
DEVICE_CLASS_USERAUTH = 0x1 << 8, /** UserAuth device */
DEVICE_CLASS_MAX = 0x1 << 9, /** Maximum value of a device class */
} DeviceClass;
C++侧使用
#include <iservmgr_hdi.h>
#define FOO_SERVICE_NAME "foo_service"
using OHOS::sptr;
using OHOS::HDI::ServiceManager::V1_0::IServiceManager;
using OHOS::HDI::ServiceManager::V1_0::ServStatListenerStub;
class FooListener : public ServStatListenerStub {
public:
FooListener() = default;
~FooListener() = default;
void OnReceive(const ServiceStatus &status) override {
if (status.serviceName != FOO_SERVICE_NAME) {
continue;
}
// 获取到服务状态信息
}
};
int main()
{
sptr<IServiceManager> servmgr = IServiceManager::Get(); // 获取服务管理对象
sptr<FooListener> listener = new FooListener(); // 创建监听对象
int32_t ret = servmgr->RegisterServiceStatusListener(listener, DEVICE_CLASS_DEFAULT); // 注册监听对象
ret = servmgr->UnregisterServiceStatusListener(listener); // 注销监听对象
return 0;
}
C侧使用:
#include <string.h>
#include <servmgr_hdi.h>
#include <servstat_listener_hdi.h>
#define FOO_SERVICE_NAME "foo_service"
// 监听回调函数
static void OnServiceStatusReceived(struct ServiceStatusListener *listener, struct ServiceStatus *servstat)
{
if (strcmp(servstat->serviceName, FOO_SERVICE_NAME) == 0) {
// ...
}
}
int main()
{
struct HDIServiceManager *servmgr = HDIServiceManagerGet(); // 创建服务管理对象
struct ServiceStatusListener *listener = HdiServiceStatusListenerNewInstance(); // 创建监听对象
listener->callback = OnServiceStatusReceived; // 设置监听回调函数
int32_t ret = servmgr->RegisterServiceStatusListener(servmgr, listener, DEVICE_CLASS_DEFAULT); // 注册监听对象
ret = servmgr->UnregisterServiceStatusListener(servmgr, listener); // 注销监听对象
HdiServiceStatusListenerFree(listener); // 释放监听对象
return 0;
}
HDI服务提供动态加载能力,系统启动过程中默认不加载,支持手动加载,以下为示例:
device_info.hcs
foo :: host {
hostName = "foo_host";
priority = 50;
foo_device :: device {
device0 :: deviceNode {
policy = 2;
priority = 100;
proload = 2; // 设置proload为2,则系统启动过程中默认不加载,后续可手动加载
moduleName = "libfoo_driver.z.so";
serviceName = "foo_service";
}
}
}
C++侧接口使用示例:
#include <idevmgr_hdi.h>
#include <v1_0/ifoo.h>
#define FOO_SERVICE_NAME "foo_service"
using OHOS::sptr;
using OHOS::HDI::DeviceManager::V1_0::IDeviceManager;
using OHOS::HDI::Foo::V1_0::IFoo;
int main() {
sptr<IDeviceManager> devmgr = IDeviceManager::Get(); // 获取HDI设备管理服务对象
int32_t ret = devmgr->LoadDevice(FOO_SERVICE_NAME); // 加载设备服务
sptr<IFoo> fooService = IFoo::Get(false); // 获取HDI服务对象
ret = fooService->Ping(); // 调用HDI接口
ret = devmgr->UnloadDevice(FOO_SERVICE_NAME); // 卸载设备服务
return 0;
}
C侧接口使用示例:
#include <devmgr_hdi.h>
#include <v1_0/ifoo.h>
#define FOO_SERVICE_NAME "foo_service"
int main()
{
struct HDIDeviceManager *devmgr = HDIDeviceManagerGet(); // 获取HDI设备管理服务对象
int32_t ret = devmgr->LoadDevice(devmgr, FOO_SERVICE_NAME); // 加载设备服务
struct IFoo *fooService = IFooGet(false); // 获取HDI服务对象
ret = fooService->Ping(); // 调用HDI接口
IFooRelease(fooService, false); // 释放HDI服务对象
ret = devmgr->UnloadDevice(devmgr, FOO_SERVICE_NAME); // 卸载设备服务
HDIDeviceManagerRelease(devmgr); // 释放设备管理服务对象
return 0;
}
HDI调试主要包括:
按顺序进行排查相关问题。
HDI服务进程启动是否正常,主要看HDI服务进程是否存在,参照以下步骤:
HDI服务加载是否正常,主要看HDI服务加载成功,排查步骤如下:
查看驱动服务加载的hilog日志:驱动服务加载日志
使用进程号,过滤出服务进程日志,加载正常的日志如下,仅供参考:
08-22 10:34:38.110 467 467 I C02500/hdf_device_host: hdf device host foo_host 21 start // host服务开启日志 "foo_host"为hostName
08-22 10:34:38.311 467 467 I C02500/devsvc_manager_proxy: DevSvcManagerProxyGetService finish, and status is 0
08-22 10:34:38.332 467 467 I C02500/devhost_service_stub: add device 0x15000101
08-22 10:34:38.400 467 467 I C02500/hcs_blob_if: CheckHcsBlobLength: the blobLength: 27930, byteAlign: 0
08-22 10:34:38.400 467 467 I C02500/device_node: launch devnode foo_service // 开始加载驱动服务
08-22 10:34:38.400 467 467 I C02500/HDF_LOG_TAG: HdfFooDriverBind enter // 调用bind函数
08-22 10:34:38.568 467 467 E C02500/load_hdi: LoadHdiImpl failed to get symbol of '<private>', Symbol not found: FooImplRelease, version: null // 此日志不影响HDI-C++服务加载
08-22 10:34:38.568 467 467 I C02500/HDF_LOG_TAG: HdfFooDriverInit enter // 调用init函数
08-22 10:34:38.602 467 467 I C02500/devsvc_manager_proxy: servmgr add service foo_service, result is 0
08-22 10:34:38.612 467 467 I C02500/devmgr_service_proxy: Attach device host dispatch finish, status is 0 // 加载成功
08-22 10:34:38.613 467 467 I C02500/hdf_power_manager: HdfPmTaskQueueInit HdfTaskQueueCreate success
方法1
方法2
HDI接口调用测试大部分属于各模块业务范围,自行处理。
编译问题一般会给出具体报错信息,如无法定位,请联系工具开发者,下面给出常见问题示例:
出现以下报错:
[OHOS ERROR] [HDI-GEN]: invailed file path '/home/xxx/codespace/master_9_7/drivers/interface/foo/v1_0/IFoo.idl'.
文件路径检查无误,查看idl文件属性:
master$ ll ./drivers/interface/foo/v1_0/
drwxr-xr-x 2 xxx xxx 4096 Sep 8 16:12 ./
drwxr-xr-x 3 xxx xxx 4096 Sep 8 16:10 ../
-rw-r--r-- 1 xxx xxx 1045 Sep 9 11:14 BUILD.gn
-rw-r--r-- 1 xxx xxx 103 Sep 8 16:37 IBar.idl
-rw-r--r-- 1 nobody nogroup 349 Sep 9 11:17 IFoo.idl # 文件拥有者和所属组错误
-rw-r--r-- 1 xxx xxx 111 Sep 9 11:22 IFooCallback.idl
-rw-r--r-- 1 xxx xxx 181 Sep 9 11:17 Types.idl
修改idl文件拥有者和所属组即可,chown xxx:xxx ./drivers/interface/foo/v1_0/IFoo.idl
一般为hdi-gen工具未重新编译导致,以master分支、rk3568为例,删除以下目录,重新编译即可
//out/rk3568/obj/drivers/framework/tools/hdi-gen
服务进程拉起失败一般与hdf_devhost.cfg、passwd、group、以及selinux相关,dmesg查看内核日志,搜索hostName,查找失败日志进行定位,以下为常见问题:
内核日志中出现如下报错:
[ 6.468021] [pid=1][Init][INFO][init_config.c:37]ParseInitCfg /vendor/etc/init/hdf_devhost.cfg
[ 6.472081] [pid=1][Init][ERROR][init_utils.c:89]Failed to decode uid for foo_host
[ 6.472110] [pid=1][Init][ERROR][init_service_manager.c:887]Failed to get uid for service foo_host
检查/system/etc/passwd和/system/etc/group文件,是否未配置或配置错误,修改或添加配置即可,文件对应路径如下:
代码路径 | 打包路径 | 配置路径 |
---|---|---|
//base/startup/init/services/etc/passwd | //out/rk3568/packages/phone/system/etc/passwd | /system/etc/passwd |
//base/startup/init/services/etc/group | //out/rk3568/packages/phone/system/etc/group | /system/etc/group |
内核日志中出现如下报错:
[ 6.472110] [pid=1][Init][ERROR][init_common_service.c:224]failed to set service foo_host's secon (u:r:foo_host:s0).
出现此种情况,一般为未配置type.te或配置错误,type.te配置详见selinux配置章节:
修改type.te
//base/security/selinux/sepolicy/ohos_policy/drivers/adapter/vendor/type.te
修改此文件并编译完成后,将其生成的文件
//out/rk3568/packages/phone/system/etc/selinux/targeted/policy/policy.31
push到板子对应目录下:
/system/etc/selinux/targeted/policy/policy.31
重启板子即可
master分支需配置selinux,可能会出现HDI服务进程因权限不足导致进程拉起失败,具体处理办法如下:
由于不同模块的HDI服务所需的进程权限不同,只能按需配置,一般建议先将selinux关闭,待业务接口调试通过后,再开启selinux,消除权限问题。
将/system/etc/selinux/config文件取出,设置SELINUX为permission,再替换掉原来的配置,重启即可
# Copyright (c) 2021 鍖椾含涓囬噷绾㈢鎶€鏈夐檺鍏徃
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
SELINUX=permissive
业务调试通过后,打开selinux,通过dmesg | grep avc | grep [hostName],过滤出与本进程相关的avc告警,依据此告警信息,添加对应的selinux配置项
device_info.hcs中hostName可设置进程名,hostName设置过长会出现hostName默认为“hdf_devhost”的情况。
例如当设置hostName为“foo_too_long_too_long_host”时:
$ps -ef | grep -v "grep" | grep foo
foo_too_long_too_long_host 529 1 0 06:59:17 ? 00:00:00 hdf_devhost 21 foo_too_long_too_long_host
查看hilog启动日志,出现如下报错:
08-17 06:59:18.905 529 529 E C02500/hdf_device_host: failed to set new process title because the 'foo_too_long_too_long_host' is too long
一般设置hostName不超过16字节
当hilog中未搜索到驱动加载的相关日志,一般为hcs文件未参与编译生效,grep -a [驱动服务名] /vendor/etc/hdfconfig/hdf_default.hcb,若搜索不到结果,则为device_info.hcs文件修改后,其相关编译产物hdf_default.hcb文件未重新编译导致,需要删除out目录下的hcb和cfg文件后重新编译:
find ./out/ -name "hdf_default.hcb" -o -name "hdf_devhost.cfg" | xargs rm -f
将生成的以下两个文件:
//out/rk3568/packages/phone/vendor/etc/hdfconfig/hdf_default.hcb
//out/rk3568/packages/phone/vendor/etc/init/hdf_devhost.cfg
替换目录下的对应文件:
/vendor/etc/hdfconfig/hdf_default.hcb
/vendor/etc/init/hdf_devhost.cfg
替换后重启,查看hillog日志继续排查
出现以下报错:
driver_loader_full: /vendor/lib/libfoo_driver.z.so no valid, errno:2
此处的libfoo_driver.z.so是hdf_devmgr进程通过读取hdf_default.hcb(hdf_default.hcb是通过hcs文件编译生成)文件获取(此处的libfoo_driver.z.so为hcs驱动配置中的moduleName值),此报错是由于找不到对应的drivers库文件,有两个原因:
service库指接口实现库,驱动服务加载时,bind函数中会调用IFoo::Get(true)以dlopen的方式获取service对象,而dlopen的库名采用接口描述和服务名转换而来,即需按一定规则对service库进行命名,当命名不规范是,会出现以下错误日志:
08-22 11:22:51.046 1816 1816 I C02500/HDF_LOG_TAG: HdfFooDriverBind enter
08-22 11:22:51.046 1816 1816 E C02500/load_hdi: ParseInterface invalid hdi impl so name /vendor/lib/libfoo_service_1.0.z.so // 这里的库名为转换拼接而成,需要和提供的service库名相等
08-22 11:22:51.047 1816 1816 E C02500/load_hdi: failed to parse hdi interface info from 'ohos.hdi.foo.v1_0.IFoo'
08-22 11:22:51.047 1816 1816 E C02500/foo_stub: failed to load hdi impl ohos.hdi.foo.v1_0.IFoo
08-22 11:22:51.047 1816 1816 E C02500/HDF_LOG_TAG: HdfFooDriverBind: failed to get of implement service // 获取服务实现对象失败
service库命名规则参考:接口实现库命名规则
当上层服务调用Get接口获取HDI服务对象时,其对应的Host服务还未被拉起,就会出现此种情况,日志入下:
07-05 11:56:05.323 244 253 E 02500/devsvc_manager_stub: service input_interfaces_service not found # 服务管理查询host服务失败
07-05 11:56:08.282 520 520 I 02500/device_node: launch devnode input_interfaces_service # 开始加载host服务
07-05 11:56:11.994 244 251 I 02500/servstat: notify service status input_interfaces_service
07-05 11:56:12.907 244 251 I 02500/servstat: notify service status input_interfaces_service
解决办法:
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。