OpenHarmony基于HDF简单驱动开发实例
背景
OpenHarmony-3.0-LTS
qemu_small_system_demo
liteos_a
qemu
添加配置
device/qemu/arm_virt/liteos_a/hdf_config/device_info/device_info.hcs
device_info 新增:
sample_host :: host {
hostName = "sample_host";
sample_device :: device {
device0 :: deviceNode {
policy = 2;
priority = 100;
preload = 1;
permission = 0664;
moduleName = "sample_driver";
serviceName = "sample_service";
}
}
}
添加驱动代码
目录:device/qemu/arm_virt/liteos_a/drivers
新建驱动实现
mkdir sample_driver
vim sample_driver/sample_driver.c
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include "hdf_log.h"
#include "hdf_base.h"
#include "hdf_device_desc.h"
#define HDF_LOG_TAG sample_driver
#define SAMPLE_WRITE_READ 123
static int32_t HdfSampleDriverDispatch( \
struct HdfDeviceIoClient *client, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
{
HDF_LOGI("%{public}s: received cmd %{public}d", __func__, id);
if (id == SAMPLE_WRITE_READ) {
const char *readData = HdfSbufReadString(data);
if (readData != NULL) {
HDF_LOGE("%{public}s: read data is: %{public}s", __func__, readData);
}
if (!HdfSbufWriteInt32(reply, INT32_MAX)) {
HDF_LOGE("%{public}s: reply int32 fail", __func__);
}
return HdfDeviceSendEvent(client->device, id, data);
}
return HDF_FAILURE;
}
static void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
{
// release resources here
return;
}
static int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
{
if (deviceObject == NULL) {
return HDF_FAILURE;
}
static struct IDeviceIoService testService = {
.Dispatch = HdfSampleDriverDispatch,
};
deviceObject->service = &testService;
return HDF_SUCCESS;
}
static int HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
{
if (deviceObject == NULL) {
HDF_LOGE("%{public}s::ptr is null!", __func__);
return HDF_FAILURE;
}
HDF_LOGI("Sample driver Init success");
return HDF_SUCCESS;
}
static struct HdfDriverEntry g_sampleDriverEntry = {
.moduleVersion = 1,
.moduleName = "sample_driver",
.Bind = HdfSampleDriverBind,
.Init = HdfSampleDriverInit,
.Release = HdfSampleDriverRelease,
};
HDF_INIT(g_sampleDriverEntry);
新建Makefile和BUILD.gn
新建Makefile和BUILD.gn,可参考其他平台,例如 device/hisilicon/drivers/rtc/
Makefile:
include $(LITEOSTOPDIR)/config.mk
include $(LITEOSTOPDIR)/../../drivers/adapter/khdf/liteos/lite.mk
MODULE_NAME := sample_driver
LOCAL_CFLAGS += $(HDF_INCLUDE)
LOCAL_SRCS += sample_driver.c
LOCAL_CFLAGS += -fstack-protector-strong -Wextra -Wall -Werror -fsigned-char -fno-strict-aliasing -fno-common
include $(HDF_DRIVER)
BUILD.gn:
import("//drivers/adapter/khdf/liteos/hdf.gni")
#module_switch = defined(LOSCFG_DRIVERS_HDF_PLATFORM_RTC)
module_name = "sample_driver"
hdf_driver(module_name) {
sources = [ "sample_driver.c" ]
}
修改上层BUILD.gn
import("//drivers/adapter/khdf/liteos/hdf.gni")
group("drivers") {
public_deps = [ "../../../drivers" ]
#新增
deps = [
"sample_driver",
]
}
config("public") {
configs = [ "../../../drivers:public" ]
}
编写驱动交互代码
目录:vendor/ohemu/qemu_small_system_demo/
新建用户代码实现
mkdir hdf_test
vim hdf_test/sample_service.c
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "hdf_log.h"
#include "hdf_io_service_if.h"
#define HDF_LOG_TAG sample_test
#define SAMPLE_SERVICE_NAME "sample_service"
#define SAMPLE_WRITE_READ 123
int g_replyFlag = 0;
static int OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *data)
{
const char *string = HdfSbufReadString(data);
if (string == NULL) {
HDF_LOGE("fail to read string in event data");
g_replyFlag = 1;
return HDF_FAILURE;
}
HDF_LOGI("%{public}s: dev event received: %{public}u %{public}s", (char *)priv, id, string);
g_replyFlag = 1;
return HDF_SUCCESS;
}
static int SendEvent(struct HdfIoService *serv, char *eventData)
{
int ret = 0;
struct HdfSBuf *data = HdfSBufObtainDefaultSize();
if (data == NULL) {
HDF_LOGE("fail to obtain sbuf data");
return 1;
}
struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
if (reply == NULL) {
HDF_LOGE("fail to obtain sbuf reply");
ret = HDF_DEV_ERR_NO_MEMORY;
goto out;
}
if (!HdfSbufWriteString(data, eventData)) {
HDF_LOGE("fail to write sbuf");
ret = HDF_FAILURE;
goto out;
}
ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);
if (ret != HDF_SUCCESS) {
HDF_LOGE("fail to send service call");
goto out;
}
int replyData = 0;
if (!HdfSbufReadInt32(reply, &replyData)) {
HDF_LOGE("fail to get service call reply");
ret = HDF_ERR_INVALID_OBJECT;
goto out;
}
HDF_LOGI("Get reply is: %{public}d", replyData);
out:
HdfSBufRecycle(data);
HdfSBufRecycle(reply);
return ret;
}
int main()
{
char *sendData = "default event info";
HDF_LOGI(SAMPLE_SERVICE_NAME "start");
struct HdfIoService *serv = HdfIoServiceBind(SAMPLE_SERVICE_NAME);
if (serv == NULL) {
HDF_LOGE("fail to get service %s", SAMPLE_SERVICE_NAME);
return HDF_FAILURE;
}
static struct HdfDevEventlistener listener = {
.callBack = OnDevEventReceived,
.priv ="Service0"
};
if (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS) {
HDF_LOGE("fail to register event listener");
return HDF_FAILURE;
}
if (SendEvent(serv, sendData)) {
HDF_LOGE("fail to send event");
return HDF_FAILURE;
}
while (g_replyFlag == 0) {
sleep(1);
}
if (HdfDeviceUnregisterEventListener(serv, &listener)) {
HDF_LOGE("fail to unregister listener");
return HDF_FAILURE;
}
HdfIoServiceRecycle(serv);
return HDF_SUCCESS;
}
新建BUILD.gn
vim sample_service/BUILD.gn,可参考huawei/hdf/sample/platform/uart/dispatch/BUILD.gn
import("//build/lite/config/component/lite_component.gni")
HDF_FRAMEWORKS = "//drivers/framework"
lite_component("hdf_test") {
features = [ ":sample_service" ]
}
executable("sample_service") {
sources = [ "sample_service.c" ]
include_dirs = [
"$HDF_FRAMEWORKS/ability/sbuf/include",
"$HDF_FRAMEWORKS/core/shared/include",
"$HDF_FRAMEWORKS/core/host/include",
"$HDF_FRAMEWORKS/core/master/include",
"$HDF_FRAMEWORKS/include/core",
"$HDF_FRAMEWORKS/include/utils",
"$HDF_FRAMEWORKS/utils/include",
"$HDF_FRAMEWORKS/include/osal",
"//drivers/adapter/uhdf/posix/include",
"//third_party/bounds_checking_function/include",
"//base/hiviewdfx/hilog_lite/interfaces/native/innerkits",
]
deps = [
"//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared",
"//drivers/adapter/uhdf/manager:hdf_core",
"//drivers/adapter/uhdf/posix:hdf_posix_osal",
]
public_deps = [ "//third_party/bounds_checking_function:libsec_shared" ]
defines = [ "__USER__" ]
cflags = [
"-Wall",
"-Wextra",
"-Wno-format",
"-Wno-format-extra-args",
]
}
说明: 用户态应用程序使用了HDF框架中的消息发送接口,因此在编译用户态程序的过程中需要依赖HDF框架对外提供的hdf_core和osal的动态库,在gn编译文件中添加如下依赖项:
deps = [
? "//drivers/hdf_core/adapter/uhdf/manager:hdf_core",
? "//drivers/hdf_core/adapter/uhdf/posix:hdf_posix_osal",
]
修改上层BUILD.gn
group("qemu_small_system_demo") {
deps = [
"apps",
"init_configs",
"hdf_test", #新增
]
}
以上都是趟过了许多坑后再跑通的
编译测试
使用 hb 重新编译镜像,会生成对应带新增驱动的内核和带用户程序的镜像
#编译
hb clean
hb build
运行新编译的镜像
#运行qemu
./vendor/ohemu/qemu_small_system_demo/patches/qemu-run
注: 需要修改上述脚本中的 rebuild=yes,让其每次都重新生成qemu镜像
开机log有如下打印,说明驱动已加载成功,并且在/dev/hdf/下面会生成 sample_service的节点:
...
01-01 00:00:00.119 2 4 W 02500/driver_loader: failed to load node, property is null, match attr is:
01-01 00:00:00.119 2 4 I 02500/sample_driver: Sample driver Init success
01-01 00:00:00.119 2 4 I 02500/osal_cdev: OsalRegisterCdev:register /dev/hdf/sample_service
01-01 00:00:00.119 2 4 D 02500/devmgr_service: DevmgrServiceUpdateStatus host:sample_host 0 device:sample_service 0 status:1
...
OHOS:/$ ls /dev/hdf/
dev_mgr event2 hdf_input_event1 input_dev_manager sample_service
运行测试应用 sample_service,会有如下打印:
OHOS:/$ sample_service
01-01 00:00:59.059 10 44 I 02500/sample_test: sample_servicestart
01-01 00:00:59.063 10 44 I 02500/sample_driver: HdfSampleDriverDispatch: received cmd 123
01-01 00:00:59.063 10 44 E 02500/sample_driver: HdfSampleDriverDispatch: read data is: default event info
01-01 00:00:59.064 10 44 I 02500/sample_test: Get reply is: 2147483647
01-01 00:00:59.068 10 45 I 02500/sample_test: Service0: dev event received: 123 default event info
01-01 00:01:00.065 10 44 D 02500/hdf_syscall_adapter: ioctl send poll thread(4) exit event, ret=0
OHOS:/$ 01-01 00:01:00.066 10 45 I 02500/hdf_syscall_adapter: event listener task received exit event
01-01 00:01:00.066 10 45 I 02500/hdf_syscall_adapter: event listener task exit
01-01 00:01:00.067 10 44 I 02500/hdf_syscall_adapter: poll thread exited
^C
OHOS:/$ ^C
想学习更多华为鸿蒙HarmonyOS开发知识,在这里我为大家准备了华为鸿蒙HarmonyOS开发者资料大全,大家可以自行点击链接领取:《做鸿蒙应用开发到底学习些啥?》
其次就是考虑到市场上还没有系统性的学习资料,同时我也整理了一份《鸿蒙 (Harmony OS)开发学习手册》特意整理成PDF文档方式,分享给大家参考学习,大家可以根据自身情况进行获取:《做鸿蒙应用开发到底学习些啥?》
《鸿蒙 (Harmony OS)开发学习手册》
一、入门必看
1.?应用开发导读(ArkTS)
2.?应用开发导读(Java)
3.......
二、HarmonyOS 概念
1.?系统定义
2.?技术架构
3.?技术特性
4.?系统安全
5......
三、如何快速入门?《做鸿蒙应用开发到底学习些啥?》
1.?基本概念
2.?构建第一个ArkTS应用
3.?构建第一个JS应用
4.?……
四、开发基础知识
1.?应用基础知识
2.?配置文件
3.?应用数据管理
4.?应用安全管理
5.?应用隐私保护
6.?三方应用调用管控机制
7.?资源分类与访问
8.?学习ArkTS语言
9.?……
五、基于ArkTS 开发
1.?Ability开发
2.?UI开发
3.?公共事件与通知
4.?窗口管理
5.?媒体
6.?安全
7.?网络与链接
8.?电话服务
9.?数据管理
10.?后台任务(Background Task)管理
11.?设备管理
12.?设备使用信息统计
13.?DFX
14.?国际化开发
15.?折叠屏系列
16.?……
更多了解更多鸿蒙开发的相关知识可以参考:《做鸿蒙应用开发到底学习些啥?》