GPIO服务开发流程说明
1.1 开发流程
2.2.1 创建目录结构
在foundation下新增mhbase目录,手动创建如下目录:
/foundation/mhbase
├── gpio
│ ├── etc # 服务启动配置目录----配置服务启动相关信息
│ ├── frameworks # 框架实现存在目录
│ │── napi # NAPI接口目录
│ └── proxy # 客户端代理目录
│ ├── interfaces # 接口目录
│ ├── sa_profile # sa服务配置目录----配置服务的动态库信息
│ ├── services # 组件服务端目录
│ ├── utils # 工具类目录
```
2.2.2 构建代码流程
1.2 业务逻辑
GPIO服务框架为应用提供控制GPIO引脚的功能,能够对其进行读取和写入。通过IPC(进程间通信)机制实现应用与服务的跨进程通讯。应用作为请求方(Client)可获取GPIO服务(Server)提供的代理 (Proxy),并通过此代理读写数据来实现进程间的数据通信。
2. 实现步骤
2.1 在矿鸿系统上创建标准服务
3.1.1 定义接口类(Igpio)
接口类继承IRemoteBroker,定义描述符、业务函数和消息码。
路径:gpio/interfaces/innerkits/native/proxy/include/i_gpio.h
3.1.2 实现服务提供端(GpioServiceStub)
该类是和IPC框架相关的实现,需要继承自IRemoteStub<Igpio>。Stub端作为接收请求的一端,需重写OnRemoteRequest方法用于接收客户端调用。
3.1.3 实现服务请求端(GpioServiceProxy)
该类是Proxy端实现,继承自IRemoteProxy<Igpio>,调用SendRequest接口向Stub端发送请求,对外暴露服务端提供的能力。
3.1.4 注册及启动服务
SA需要将自己的GpioService实例通过AddSystemAbility接口注册到SystemAbilityManager。
GpioService继承于SystemAbility和GpioServiceStub,注册方法如下:
namespace { auto ms = DelayedSpSingleton<GpioService>::GetInstance(); const bool G_REGISTER_RESULT = SystemAbility::MakeAndRegisterAbility(ms.GetRefPtr()); }
SA获取到实例后会调用GpioService中的OnStart()方法,进行发布
Publish(DelayedSpSingleton<GpioService>::GetInstance())
3.1.5 获取服务
通过SystemAbilityManager的GetSystemAbility方法可获取到对应SA的代理IRemoteObject,参考GpioServiceClient类中方法Connect()。
GpioServiceClient中的内部类GpioDeathRecipient继承于IRemoteObject::DeathRecipient,可以订阅RemoteObject的死亡通知,当收到死亡通知时,会调用内部类中的onRemoteDie方法。
3.1.6 编写Gn文件生成 libgpioservice.z.so
矿鸿系统中大多以编写Gn文件的方式生成动态库文件,gpio服务的代码也不例外。如下是Gn文件的简单介绍,具体代码可参考gpio/services/BUILD.gn
import("//build/ohos.gni") #引用的gni路径 import("//foundation/mhbase/gpio/gpio.gni") config("gpio_service_config") { #配置模块 include_dirs = [ #动态库里要用到的头文件 ] } ohos_shared_library("gpio_service") { #生成的成动态库名称 sources = [ "src/gpio_service.cpp", "src/gpio_service_stub.cpp", "src/gpio_manager.cpp", #包含的cpp文件 ] configs = [ ":gpio_service_config" ] #包含配置模块 deps = [ #依赖的gpio子系统内的动态库 "${gpio_frameworks_path}/proxy:gpio_proxy", ] external_deps = [ #依赖gpio子系统外的动态库 "hiviewdfx_hilog_native:libhilog", #hilog动态库 "ipc:ipc_core", #ipc动态库,用于跨进程通讯 "utils_base:utils", #常用工具类动态库 "safwk:system_ability_fwk", #系统服务管理子系统 "samgr_standard:samgr_proxy", #samgr模块动态库 ] subsystem_name = " mhbase" #模块名称 part_name = "${gpio_part_name}" #子系统名称,定义在gpio/gpio.gni }
3.1.7 SA服务加载 libgpioservice.z.so
配置XML文件,路径:gpio/sa_profile/8120.xml,该文件配置了动态库的各种信息,SA服务会解析XML,同时将动态库加载到自身进程。注意GN文件需要按如下方式
import("//foundation/mhabse/gpio/gpio.gni") import("//build/ohos/sa_profile/sa_profile.gni") ohos_sa_profile("gpio_sa_profile") { sources = [ "8120.xml" ] part_name = "${gpio_part_name}" }
3.1.8 服务启动的相关配置
每次开机,init阶段会读取配置gpioservice.cfg,启动gpioservice。即拉起服务动态库。路径:gpio/etc/gpioservice.cfg
{ "jobs" : [{ "name" : "init", "cmds" : [] },{ "name" : "post-fs", "cmds" : [ "start gpioservice" ] } ], "services" : [ { "name" : "gpioservice", "path" : ["/system/bin/sa_main", "/system/profile/gpioservice.xml"], "uid" : "root", "gid" : ["system", "shell", "uhid"], "caps" : ["DAC_READ_SEARCH", "DAC_OVERRIDE", "CHOWN"], "start-mode" : "condition", "secon" : "u:r:gpioservice:s0" } ] }
注意:gpioservice.cfg文件要安装在init路径下,所以gn文件这样写:(可参考gpio/etc/BUILD.gn)
import("//build/ohos.gni") import("//foundation/mhbase/gpio/gpio.gni") ohos_prebuilt_etc("gpioservice.rc") { source = "gpioservice.cfg" relative_install_dir = "init" part_name = "gpioservice" subsystem_name = " mhbase " }
2.2 服务启动配置
3.2.1 在编译构建主目录下,增加mhbase模块信息,及子系统信息
配置文件路径:build/subsystem_config.json
"mhbase": { "path": "foundation/mhbase", "name": " mhbase" }
3.2.2 在HiLog模块增加配置子系统的hilog信息(矿鸿代码中未找到文件,未配置不影响系统启动)
HiLog 是 OpenHarmony 日志系统,提供给系统框架、服务、以及应用打印日志,记录用户操作、系统运行状态等。如果你的代码中需要用到系统级Hilog功能,则需要引用到该模块。在编译lib库时需要增加如下相关依赖:(可参考gpio/frameworks/proxy/BUILD.gn)
external_deps = [ "hiviewdfx_hilog_native:libhilog", ]
配置文件路径:base/hiviewdfx/hilog/services/hilogd/log_domains.cpp
{0xD005000, "GpioService"},
3.2.3 在samgr模块配置服务属性
samgr组件是OpenHarmony的核心组件,提供OpenHarmony系统服务启动、注册、查询等功能。如果你的代码中涉及开发服务相关功能,则需要引用到该模块。在编译lib库时需要增加如下相关依赖:(可参考gpio/frameworks/proxy/BUILD.gn)
external_deps = [ "safwk:system_ability_fwk", " samgr_standard:samgr_proxy " ]
在该模块下配置服务名称及ID: GPIO_SERVICE_SA_ID = 8120。配置文件路径:foundation/systemabilitymgr/samgr/interfaces/innerkits/samgr_proxy/include/system_ability_definition.h
GPIO_SERVICE_SA_ID = 8120, { GPIO_SERVICE_SA_ID, "GpioService"},
3.2.4
修改配置文件:productdefine/commom/products/rk3568.json
"mhservice:gpioservice": {}
3.2.5 在gpio目录下编写bundle.json文件
代码在编译时,会根据subsystem_config.json的映射(3.2.1中的配置项),扫描foundation/mhbase此目录下的所有bundle.json文件,找到子系统名为gpioservice的bundle.json(可以有多个),即参与编译的bundle.json文件为foundation/mhbase/gpioservice/bundle.json。
bundle.json中需要包含子系统中参与的编译的所有模块。
"deps": { "components": [ #编译模块依赖的其他子系统 "hiviewdfx_hilog_native", "ipc", "safwk", "samgr", "appexecfwk_standard", "napi" ], "third_party": [ #编译的模块的三方库依赖 "quickjs", "node", "libuv" ] }, "build": { "sub_component": [ #需要编译的模块 "//foundation/mhbase/gpio/frameworks/napi:gpio_napi", "//foundation/ mhbase /gpio/frameworks/proxy:gpio_proxy", "//foundation/ mhbase /gpio/sa_profile:gpio_sa_profile", "//foundation/ mhbase /gpio/services:gpio_service", "//foundation/ mhbase /gpio/etc:gpioservice.rc" ], "inner_kits": [], "test": [] }
2.3 NAPI开发
NAPI适合封装IO、CPU密集型、OS底层等能力并对外暴露JS接口,通过NAPI可以实现JS与C/C++代码互相访问。可以通过NAPI接口构建例如网络通信、串口访问、多媒体解码、传感器数据收集等模块。
我们需要按照NAPI框架的机制要求,实现注册相关动作,告诉系统这个lib库的名称,提供了哪些native方法,以及它们对应的js接口名称是什么。
具体代码可参考: gpio/frameworks/napi/src/js_gpio_napi.cpp
3.3.1 注册NAPI模块
使用__attribute__((constructor))标记函数,napi_module_register函数注册模块
extern "C" __attribute__((constructor)) void RegisterModule() { napi_module_register(&gpioModule); }
3.3.2 创建NAPI结构体
创建一个NAPI模块属性说明的结构体,重点需要关注的属性nm_register_func和nm_modname。前者需要实现Init函数,声明导出的API,后者是API的包名,供JS调用。
static napi_module gpioModule = { .nm_version = 1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = Init, .nm_modname = "gpio_napi", .nm_priv = reinterpret_cast<void*>(0), .reserved = {0}, };
3.3.3 声明要导出的API
setStatus为导出函数的名称,供JS调用。
napi_value Init(napi_env env, napi_value exports) { GPIO_LOGI("Init enter"); napi_property_descriptor desc[] = { DECLARE_NAPI_FUNCTION("setStatus", SetGpioStatus), }; CHKRP(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc), "napi_define_properties"); return exports; }
3.3.4 导出函数的具体实现
static napi_value SetGpioStatus(napi_env env, napi_callback_info info) { GPIO_LOGI("SetGpioStatus enter"); napi_value thisVar = nullptr; size_t argc = 0; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, nullptr, &thisVar, nullptr)); //获取JS传参个数 napi_value args[2] = { 0 }; // 2: args NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisVar, nullptr)); //根据参数个数,获取参数 NAPI_ASSERT(env, argc == 2, "Wrong number of arguments"); //判断参数个数是不是2个 napi_valuetype valuetype = napi_undefined; NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); //获取第一个参数的类型 NAPI_ASSERT(env, valuetype == napi_number, "Wrong argument info. Object expected."); //判断第一个参数的类型是不是数字 int32_t gpio = -1, gpioStatus = -1; napi_get_value_int32(env, args[0], &gpio); napi_get_value_int32(env, args[1], &gpioStatus); //将参数由napi_value转换为int32_t类型 int32_t res = GpioServiceClient::GetInstance().SetGpioStatus(gpio, gpioStatus); //调用client的方法 napi_value result = nullptr; NAPI_CALL(env, napi_create_int32(env, res, &result)); //返回值传递给JS return result; }
3.3.5 生成napi lib库文件
编写GN文件生成动态库文件,注意:NAPI库文件需要安装在module路径下,否则JS可能无法调用。
ohos_shared_library("gpio_napi") { #具体内容查看文件 gpio/frameworks/napi/BUILD.gn subsystem_name = "mhbase" #模块名称 part_name = "${gpio_part_name}" #子系统名称 relative_install_dir = "module" #安装到/system/lib/module下,NAPI库必须安装到/system/lib/module这个目录 }
2.4 应用开发
3.4.1 开发工具
DevEco Studio 3.0.0.993
3.4.2 开发测试代码
// @ts-nocheck
import gpio from '@ohos.gpio_napi'; @Entry @Component struct Index { @State flag: boolean = true build() { Row() { Column() { Button('手电筒') .height(70) .width('40%') .fontSize(16) .onClick(() => { console.log("Gpio 0 start "); if (this.flag) { var res1 = gpio.setStatus(1, 1); } else { var res1 = gpio.setStatus(1, 0); } console.log("Gpio gpio_napi.SetStatus " + res1); this.flag = !this.flag }) } .width('100%') } .height('100%') } }
3.4.2 运行hap包
#安装HAP包
hdc_std -t 150100424a544434520369874e908800 install
C:\......\default\entry-default-signed.hap
#卸载HAP包
hdc_std -t 150100424a544434520369874e908800 install C:\......\default\entry-default-signed.hap
3. 注意事项
本文档仅提供开发指导,已在MHBase_std 3.1-Release分支上验证成功。具体代码请参考//foundation/mhbase/gpio
ps -ef | grep gpio 查看服务命令
cat /sys/kernel/debug/gpio 查看gpio占用
hilog | grep Gpio 查看Gpio log信息