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