OpenHarmony-5.HDI 框架

  • HDI 框架(基于OH41)

1.HDI介绍

  HDI(Hardware Device Interface,硬件设备接口)是HDF驱动框架为开发者提供的硬件规范化描述性接口,位于基础系统服务层和设备驱动层之间,是连通驱动程序和系统服务进行数据流通的桥梁,是提供给硬件系统服务开发者使用的、统一的硬件设备功能抽象接口,其目的是为系统服务屏蔽南向设备差异。

  在OpenHarmony 分层结构中,HDI位于 “基础系统服务层”和“设备抽象层(DAL)”之间。硬件设备通过DAL抽象化,并基于IDL(Interface Description Language)接口描述语言描述后,为上层应用或服务提供了规范的硬件设备接口。

  HDI 是对硬件功能的较高层次抽象接口,各类外设完成 HDI 接口定义后便只会在 HDI 的兼容性规则下进行变更,从而保证接口的稳定性。具体的驱动实现不需要再重复定义 HDI 接口,只需要按需实现即可接入系统功能。

在这里插入图片描述

2.HDI 部署

  在不同量级的 OpenHarmony 系统上,HDI 存在两种部署形态,IPC 模式和直通模式。

在这里插入图片描述

  • 轻量级 OpenHarmony 系统,出于减小系统性能负载考虑,HDI 实现为用户态共享库,由系统服务直接加载 HDI 实现到自己进程中函数调用使用。HDI 实现封装具体的用户态-内核态交互过程,当需要访问驱动程序时使用 IO Service 请求将消息通过 system call 方式调用到内核驱动实现。

  • 标准 OpenHarmony 系统,HDI 以独立服务进程方式部署,系统服务只加载 HDI 客户端实现到自己进程中,实际业务运行在独立进程中,客户端通过 IPC 与服务端交互,便于架构解耦、权限管理。

2.1.HDI接口实现

  直通模式为函数实现方式,无论调用还是实现都不需要其他组件支持即可实现。

  IPC 模式基于 OpenHarmony 系统通信框架的通用模型,但是因为驱动很多时候涉及到底层操作和多系统迁移的场景而使用C语言编写,所以驱动框架还提供了 HDI 服务的 C 语言实现的基础组件,C++实现则主要使用系统通信框架组件。

2.2.IPC模式下的调用原理

  在IPC模式下,当系统服务调用HDI接口时,通过proxy库将函数调用转换为IPC请求,将接口调用的参数进行序列化;IPC请求通过IPC框架发送到服务端,请求将被stub库先处理,然后对接口调用的参数进行反序列化,再转换成对服务实现的函数调用,从而实现接口调用过程。

3.HDI接口实现

  重点分析 IPC 模式的实现:
在这里插入图片描述
  HDI IPC 模式基于 OpenHarmony 系统通信框架的通用模型,但是因为驱动很多时候涉及到底层操作和多系统迁移的场景而使用C语言编写,所以驱动框架还提供了 HDI 服务的 C 语言实现的基础组件,C++ 实现则主要使用系统通信框架组件。

3.1.IDL接口描述语言

  IDL(Interface Description Language)是一类用来描述接口的语言,通过一种中立的方式来定义客户端与服务端均认可的编程接口,可以实现在二者间的跨进程通信(IPC)。跨进程通信意味着可以在一个进程访问另一个进程的数据,或调用另一个进程的方法。通常把应用接口提供方(供调用)称为服务端,调用方称为客户端。

  IDL先把需要传递的对象分解成操作系统能够理解的基本类型,然后根据接口声明编译,生成IPC/RPC代理(Proxy)和桩(Stub)的C/C++代码,从而为调用者提供一致的接口和调用方式。

  使用IDL语法描述HDI接口并保存为.idl文件,.idl文件在编译过程中转换为C/C++语言的函数接口声明、客户端与服务端IPC相关过程代码,开发时只需要基于生成的头文件中函数接口实现具体服务功能即可。代码生成与编译功能已经集成在hdi.gni编译模板,基于该编译模板编写idl文件的BUILD.gn就可以简单的生成客户端、服务端代码并编译为共享库。如下图所示:
在这里插入图片描述

  drivers_interface 仓库提供IDL文件:

├── README.en.md
├── README.md
├── sensor                          #sensor HDI 接口定义
│   └── v1_0                        #sensor HDI 接口 v1.0版本定义
│       ├── BUILD.gn                #sensor idl文件编译脚本
│       ├── ISensorCallback.idl     #sensor callback 接口定义idl文件
│       ├── ISensorInterface.idl    #sensor interface 接口定义idl文件
│       └── SensorTypes.idl         #sensor 数据类型定义idl文件
├── audio                           #audio HDI 接口定义
│   └── ...
├── camera                          #camera HDI接口定义
├── codec                           #codec HDI接口定义
├── display                         #display HDI接口定义
├── face_auth                       #faceauth HDI接口定义
├── format                          #format HDI接口定义
├── input                           #input HDI接口定义
├── misc                            #misc HDI接口定义
├── pinauth                         #pinauth HDI接口定义
├── usb                             #usb HDI接口定义
├── fingerprint_auth                #fingerprintauth HDI接口定义
└── wlan                            #wlan HDI接口定义

3.2.创建.idl文件

  创建对应模块/版本接口目录,初始版本定义为v1_0,如 drivers/interface/input/v1.0/目录:

-rw-r--r--. 1 iscas iscas 1.1K Dec 17 11:28 BUILD.gn
-rw-r--r--. 1 iscas iscas 2.4K Dec 17 11:28 IInputCallback.idl
-rw-r--r--. 1 iscas iscas  14K Dec 17 11:28 IInputInterfaces.idl
-rw-r--r--. 1 iscas iscas 5.4K Dec 17 11:28 InputTypes.idl
  • 定义接口IInputInterfaces.idl
 46 package ohos.hdi.input.v1_0;
 47
 48 import ohos.hdi.input.v1_0.IInputCallback;
 49 import ohos.hdi.input.v1_0.InputTypes;

 61 interface IInputInterfaces {
 75     ScanInputDevice([out] struct DevDesc[] staArr);
 89     OpenInputDevice([in] unsigned int devIndex);
 103    CloseInputDevice([in] unsigned int devIndex);
 118    GetInputDevice([in] unsigned int devIndex, [out] struct DeviceInfo devInfo);
 119    ...
 120 }
  • 如果接口中用到了自定义数据类型,将自定义类型定义到一个单独的.idl文件,如InputTypes.idl
 53 struct DevDesc {
 54     unsigned int devIndex;        /**< Device index */
 55     unsigned int devType;         /**< Device type */
 56 };
 57
 61 struct DevIdentify {
 62     unsigned short busType;       /**< Bus type */
 63     unsigned short vendor;        /**< Vendor ID */
 64     unsigned short product;       /**< Product ID */
 65     unsigned short version;       /**< Version */
 66 };
  • 如果需要从服务端回调,可以定义callback接口类,如IInputCallback.idl
 45 package ohos.hdi.input.v1_0;
 47 import ohos.hdi.input.v1_0.InputTypes;
 
 58 [callback] interface IInputCallback {
 69     EventPkgCallback([in] struct EventPackage[] pkgs, [in] unsigned int devIndex);
 79     HotPlugCallback([in] struct HotPlugEvent event);
 80 }

3.3.编写 idl文件的BUILD.gn

  drivers_interface/input/v1_0目录中添加BUILD.gn文件,内容如下:

 14 import("//build/config/components/hdi/hdi.gni")
 15
 16 if (defined(ohos_lite)) {
 17   group("libinput_proxy_1.0") {
 18     deps = []
 19     public_configs = []
 20   }
 21 } else {
 22   hdi("input") {
 23     module_name = "input_service"
 24
 25     sources = [
 26       "IInputCallback.idl",
 27       "IInputInterfaces.idl",
 28       "InputTypes.idl",
 29     ]
 30     mode = "passthrough"
 31     language = "cpp"
 32     subsystem_name = "hdf"
 33     part_name = "drivers_interface_input"
 34   }
 35 }

3.4.实现 HDI 服务

   .idl文件在编译过程中除了生成C/C++语言的函数接口声明、客户端与服务端IPC相关过程代码,还会生成驱动入口文件和驱动服务实现文件的模板(如图中间部分所示),实际开发中需要做的就是参照这两个模板根据实际业务需求将文件重新实现即可(如图右侧目录结构所示)。参考Battery IDL 接口如下图所示:
在这里插入图片描述

  在上述步骤中idl编译后将在out目录out/rk3568/gen/drivers/interfaces/input/v1_0生成中间代码。基于工具自动生成的input_interface_service.h,实现其中的服务接口。

oh41/out/rk3568/gen/drivers/interface/input/v1_0$ ls -lh
total 56K
-rw-rw-r-- 1 iscas iscas 1.7K 1217 16:16 iinput_callback.h
-rw-rw-r-- 1 iscas iscas 3.4K 1217 16:16 iinput_interfaces.h
-rw-rw-r-- 1 iscas iscas 1.1K 1217 16:16 input_callback_service.cpp
-rw-rw-r-- 1 iscas iscas 1.3K 1217 16:16 input_callback_service.h
-rw-rw-r-- 1 iscas iscas 1.7K 1217 16:16 input_interfaces_proxy.cpp
-rw-rw-r-- 1 iscas iscas 3.2K 1217 16:16 input_interfaces_service.cpp
-rw-rw-r-- 1 iscas iscas 2.7K 1217 16:16 input_interfaces_service.h
-rw-rw-r-- 1 iscas iscas 2.6K 1217 16:16 input_types.h

  基于IInputInterfaces.idl 生成iinput_interfaces.h文件,内容如下:

 16 #ifndef OHOS_HDI_INPUT_V1_0_IINPUTINTERFACES_H
 17 #define OHOS_HDI_INPUT_V1_0_IINPUTINTERFACES_H
 18
 19 #include <stdint.h>
 20 #include <string>
 21 #include <vector>
 22 #include <hdf_base.h>
 23 #include <hdi_base.h>
 24 #include "input/v1_0/iinput_callback.h"
 25 #include "input/v1_0/input_types.h"
 26
 27 namespace OHOS {
 28 namespace HDI {
 29 namespace Input {
 30 namespace V1_0 {
 31 using namespace OHOS;
 32 using namespace OHOS::HDI;
 33
 34 class IInputInterfaces : public HdiBase {
 35 public:
 36     DECLARE_HDI_DESCRIPTOR(u"ohos.hdi.input.v1_0.IInputInterfaces");
 37
 38     virtual ~IInputInterfaces() = default;
 39
 40     static sptr<OHOS::HDI::Input::V1_0::IInputInterfaces> Get(bool isStub = false);
 41     static sptr<OHOS::HDI::Input::V1_0::IInputInterfaces> Get(const std::string &serviceName, bool isStub = false);
 42
 43     virtual int32_t ScanInputDevice(std::vector<OHOS::HDI::Input::V1_0::DevDesc>& staArr) = 0;
 44
 45     virtual int32_t OpenInputDevice(uint32_t devIndex) = 0;
 46
 47     virtual int32_t CloseInputDevice(uint32_t devIndex) = 0;
 48
 49     virtual int32_t GetInputDevice(uint32_t devIndex, OHOS::HDI::Input::V1_0::DeviceInfo& devInfo) = 0;
 50
        ...
 97 };
 98 } // V1_0
 99 } // Input
100 } // HDI
101 } // OHOS
102
103 #endif // OHOS_HDI_INPUT_V1_0_IINPUTINTERFACES_H

3.4.1.实现HDI服务接口

  基于工具自动生成的input_interface_service.h(继承IInputInterfaces),实现其中的服务接口,并将相关源码编译为 libinput_interfaces_service_1.0.z.so。实现代码如下:

  • drivers_peripheral\input\hdi_service\input_interfaces_impl.h:
#ifndef OHOS_HDI_INPUT_V1_0_INPUTINTERFACEIMPL_H
#define OHOS_HDI_INPUT_V1_0_INPUTINTERFACEIMPL_H

#include "input_manager.h"
#include "v1_0/iinput_interfaces.h"

namespace OHOS {
namespace HDI {
namespace Input {
namespace V1_0 {
class InputInterfacesImpl : public IInputInterfaces {
public:
    InputInterfacesImpl(): inputInterface_(NULL) {}
    virtual ~InputInterfacesImpl()
    {
        ReleaseInputInterface(&inputInterface_);
    }
    void Init();
    int32_t ScanInputDevice(std::vector<DevDesc> &staArr) override;
    int32_t OpenInputDevice(uint32_t devIndex) override;
    int32_t CloseInputDevice(uint32_t devIndex) override;
    int32_t GetInputDevice(uint32_t devIndex, DeviceInfo &devInfo) override;
    int32_t GetInputDeviceList(uint32_t &devNum, std::vector<DeviceInfo> &devList, uint32_t size) override;
    ...
private:
    IInputInterface *inputInterface_;
};
} // V1_0
} // Input
} // HDI
} // OHOS

#endif // OHOS_HDI_INPUT_V1_0_INPUTINTERFACEIMPL_H
  • drivers_peripheral\input\hdi_service\input_interfaces_impl.cpp:
int32_t InputInterfacesImpl::ScanInputDevice(std::vector<DevDesc> &staArr)
{
    if (inputInterface_ == nullptr || inputInterface_->iInputManager == nullptr ||
        inputInterface_->iInputManager->ScanInputDevice == nullptr) {
        HDF_LOGE("%{public}s: get input device Module instance failed", __func__);
        return HDF_FAILURE;
    }

    InputDevDesc staArrHdf[MAX_DEVICES];
    int32_t ret = memset_s(staArrHdf, MAX_DEVICES * sizeof(InputDevDesc), 0, MAX_DEVICES * sizeof(InputDevDesc));

    ret = inputInterface_->iInputManager->ScanInputDevice(staArrHdf, MAX_DEVICES);

    for (uint32_t i = 0; i < MAX_DEVICES; i++) {
        DevDesc StaArr;
        StaArr.devIndex = staArrHdf[i].devIndex;
        StaArr.devType = staArrHdf[i].devType;
        staArr.push_back(StaArr);
    }
    return ret;
}

int32_t InputInterfacesImpl::OpenInputDevice(uint32_t devIndex)
{
    if (inputInterface_ == nullptr || inputInterface_->iInputManager == nullptr ||
        inputInterface_->iInputManager->OpenInputDevice == nullptr) {
        HDF_LOGE("%{public}s: get input device Module instance failed", __func__);
        return HDF_FAILURE;
    }

    int32_t ret = inputInterface_->iInputManager->OpenInputDevice(devIndex);
    return ret;
}

3.4.2.实现驱动入口

  HDI服务发布是基于用户态HDF驱动框架,所以需要实现一个驱动入口。驱动实现代码参考已经在out目录中生成,可以根据业务需要直接使用该文件或参考该文件按业务需要重新实现, 然后将驱动入口源码编译为libinput_driver.z.so(该名称无强制规定,与hcs配置中配套即可)。

  • drivers_peripheral\input\hdi_service\input_interfaces_driver.cpp:
struct HdfDriverEntry g_inputinterfacesDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "input_service",
    .Bind = HdfInputInterfacesDriverBind,
    .Init = HdfInputInterfacesDriverInit,
    .Release = HdfInputInterfacesDriverRelease,
};

static int HdfInputInterfacesDriverBind(struct HdfDeviceObject *deviceObject)
{
    HDF_LOGI("HdfInputInterfacesDriverBind enter");
    auto *hdfInputInterfacesHost = new (std::nothrow) HdfInputInterfacesHost;

    hdfInputInterfacesHost->ioService.Dispatch = InputInterfacesDriverDispatch;  // 服务回调接口
    hdfInputInterfacesHost->ioService.Open = nullptr;
    hdfInputInterfacesHost->ioService.Release = nullptr;

    auto serviceImpl = IInputInterfaces::Get(true);

    hdfInputInterfacesHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl,
        IInputInterfaces::GetDescriptor());

    deviceObject->service = &hdfInputInterfacesHost->ioService;
    return HDF_SUCCESS;
}

  实现服务响应接口InputInterfacesDriverDispatch: 当收到 HDI 调用时,服务响应接口InputInterfacesDriverDispatch将会被调用。参数如下:

  • client 调用者对象;
  • cmdId 调用命令字,用于区分调用的 API;
  • data 调用入参序列化对象,在 IPC 调用场景为 parcel 对象的 C 语言封装,入参需要使用序列化接口从 data 对象中获取后再使用;
  • reply 调用出参对象,需要返回给调用的信息写入该序列化对象。
static int32_t InputInterfacesDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    ...
    // 将接口调用转发到stub实现
    return hdfInputInterfacesHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option); 
}

3.4.3.发布服务

  在产品hcs配置中声明HDI服务,以标准系统rk3568为例,HDF设备配置路径为vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs,在其中新增以下配置:

310             input_hdi_device :: device {
311                 device0 :: deviceNode {
312                     policy = 2;
313                     priority = 100;
314                     moduleName = "libinput_driver.z.so";
315                     serviceName = "input_interfaces_service";
316                 }
317             }

3.4.4.HDI使用

  • 在需要使用HDI服务的客户端BUILD.gn中增加依赖: “drivers_interface_input:libinput_proxy_1.0”
 powermgr/battery_manager/charger/BUILD.gn:
 60   external_deps = [
 61     "c_utils:utils",
 62     "config_policy:configpolicy_util",
 63     "drivers_interface_battery:libbattery_proxy_2.0",
 64     "drivers_interface_input:libinput_proxy_1.0",
 65     "init:libbegetutil",
 66     "ipc:ipc_core",
 67   ]
  • 在代码中调用HDI接口
base/powermgr/battery_manager/charger/src/charger_thread.cpp:
339 void ChargerThread::InitInput()
340 {
341     inputInterface = nullptr;
342     inputInterface = HDI::Input::V1_0::IInputInterfaces::Get(true);
347
348     const uint32_t POWERKEY_INPUT_DEVICE = 2;
349     int32_t ret = inputInterface->OpenInputDevice(POWERKEY_INPUT_DEVICE);
355
356     uint32_t devType = INDEV_TYPE_UNKNOWN;
357     ret = inputInterface->GetDeviceType(POWERKEY_INPUT_DEVICE, devType);
363
364     /* first param is powerkey input device, refer to device node '/dev/hdf_input_event2', so pass 2 */
365     if (g_callback == nullptr) {
366         g_callback = new (std::nothrow) HdfInputEventCallback();
367     }
372     ret = inputInterface->RegisterReportCallback(POWERKEY_INPUT_DEVICE, g_callback);
378 }

refer to

  • https://gitee.com/openharmony/drivers_interface
  • https://laval.csdn.net/67341a85cd8b2677c3df7942.html
  • https://ost.51cto.com/posts/8171
  • https://gitee.com/openharmony/ability_idl_tool
  • https://baijiahao.baidu.com/s?id=1731328576146856524&wfr=spider&for=pc
  • https://developer.huawei.com/consumer/cn/forum/topic/0204858485287900172
  • https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/IDL/idl-guidelines.md#%E5%88%9B%E5%BB%BAidl%E6%96%87%E4%BB%B6
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值