#2020征文-开发板#【AI Camera连载】HDF驱动和应用编译框架梳理
参考文档
链接:https://gitee.com/openharmony/docs/blob/master/driver/HDF%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6.md
HDF驱动框架
● HDF开发概述
● 驱动开发
● 驱动服务管理
● 驱动消息机制管理
● 配置管理
● HDF开发实例
HDF(OpenHarmony Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。
1. 配置管理
驱动配置包含两部分,HDF框架定义的驱动设备描述(必选)和驱动的私有配置信息(可选)。
HDF框架加载驱动所需要的信息来源于HDF框架定义的device_info.hcs配置文件(驱动设备描述)。
如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息,HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject 中的property里面,通过Bind和Init(参考驱动开发)传递给驱动。配置信息定义之后,需要将该配置文件添加到板级配置入口文件hdf.hcs。
HCS(HDF Configuration Source)是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。
在HDF框架的配置文件中添加该驱动的配置信息,如下所示:
$ vi ~/harmony/sdk/vendor/hisi/hi35xx/hi3516dv300/config/device_info/device_info.hcs
root {
device_info {
match_attr = "hdf_manager";
template host {
hostName = "";
priority = 100;
template device {
template deviceNode {
policy = 0;
priority = 100;
preload = 0;
permission = 0664;
moduleName = "";
serviceName = "";
deviceMatchAttr = "";
}
}
}
++++
sample_host :: host {
hostName = "sample_host"; // host名称,host节点是用来存放某一类驱动的容器
sample_device :: device { // sample设备节点
device0 :: deviceNode { // sample驱动的DeviceNode节点
policy = 2; // 驱动服务发布的策略:驱动对内核态和用户态都发布服务
priority = 100; // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序
preload = 0; // 0=DEVICE_PRELOAD_ENABLE,系统启动过程中默认加载驱动。
// 1=DEVICE_PRELOAD_DISABLE,系统启动过程中默认不加载驱动。
permission = 0664; // 驱动创建设备节点权限
moduleName = "sample_driver"; // 驱动和设备提供的服务相关联
serviceName = "sample_service"; // 用户空间通过服务名即可找到对应设备的驱动进行操作
//deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等
}
}
}
++++
}
}
#if 0
// 补充:
// 驱动有私有配置:
root {
SampLEDriverConfig {
sample_version = 1;
sample_bus = "I2C_0";
match_attr = "sample_config"; //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
}
}
#endif
HDF框架定了驱动对外发布服务的策略,是由配置文件中的policy字段来控制,policy字段的取值范围以及含义如下:
typedef enum {
/* 驱动不提供服务 */
SERVICE_POLICY_NONE = 0,
/* 驱动对内核态发布服务 */
SERVICE_POLICY_PUBLIC = 1,
/* 驱动对内核态和用户态都发布服务 */
SERVICE_POLICY_CAPACITY = 2,
/* 驱动服务不对外发布服务,但可以被订阅 */
SERVICE_POLICY_FRIENDLY = 3,
/* 驱动私有服务不对外发布服务,也不能被订阅 */
SERVICE_POLICY_PRIVATE = 4,
/* 错误的服务策略 */
SERVICE_POLICY_INVALID
} ServicePolicy;
2. 编写驱动代码
2.1 基于HDF框架编写的sample驱动代码如下:
$ mkdir -p ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample/hdf_sample.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
// 设置cmd编号,类似于Linux的ioctl命令码。
#define SAMPLE_WRITE_READ 123
#define SELECT_DEFAULT 0
// Dispatch是驱动中用来处理用户态发下来的消息的函数。
//int32_t HdfSampleDriverDispatch( struct HdfDeviceObject *deviceObject, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
int32_t HdfSampleDriverDispatch(struct HdfDeviceIoClient *deviceIoClient, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
{
HDF_LOGE("%s: received cmd %d", __func__, id);
if (id == SAMPLE_WRITE_READ) { // 通常会采用switch case的方式来写。
const char *readData = HdfSbufReadString(data); // 内核从用户空间获取数据,类似Linux的copy_from_user。
if (readData != NULL) {
HDF_LOGE("%s: read data is: %s", __func__, readData); // 内核打印从用户空间独到的数据。
}
#if SELECT_DEFAULT
// do nothing
#else
// 修改用户空发过来的数据缓冲区指针data的内容,实际上修改无效
char *readDataChange = "come from kernel sapce: change default event info";
if (!HdfSbufWriteString(data, readDataChange)) {
HDF_LOGE("%s: fail to write sbuf", __func__);
}
#endif
#if SELECT_DEFAULT
// 默认向用户空间返回的是INT32_MAX=2147483647
if (!HdfSbufWriteInt32(reply, INT32_MAX)) { // 内核向用户空间返回数据,类似Linux的copy_to_user。
HDF_LOGE("%s: reply int32 fail", __func__);
}
#else
// 将向用户空间返回内容修改为字符串
char *sendData = "come from kernel sapce: default event info";
if (!HdfSbufWriteString(reply, sendData)) {
HDF_LOGE("%s: fail to write sbuf", __func__);
}
#endif
// 通HdfDeviceSendEvent函数触发用户空间获取驱动上报数据。
// return HdfDeviceSendEvent(deviceObject, id, data);
return HdfDeviceSendEvent(deviceIoClient->device, id, data);
}
return HDF_FAILURE;
}
// 驱动资源释放的接口,本例未分配需要回收的资源,因此为空。
void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
{
// release resources here
return;
}
// 用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息。
int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
{
if (deviceObject == NULL) {
return HDF_FAILURE;
}
static struct IDeviceIoService testService = {
// Dispatch是用来处理用户态发下来的消息。
.Dispatch = HdfSampleDriverDispatch,
};
deviceObject->service = &testService;
return HDF_SUCCESS;
}
#if 0
// 补充:
// HdfSampleDriverBind中除了常用的IDeviceIoService,这里还可以扩展其他服务。
struct ISampleDriverService {
struct IDeviceIoService ioService; // 服务结构的首个成员必须是IDeviceIoService类型的成员
int32_t (*ServiceA)(void); // 驱动的第一个服务接口
int32_t (*ServiceB)(uint32_t inputCode); // 驱动的第二个服务接口,有多个可以依次往下累加
};
驱动服务接口的实现
int32_t SampleDriverServiceA(void)
{
// 驱动开发者实现业务逻辑
return 0;
}
int32_t SampleDriverServiceB(uint32_t inputCode)
{
// 驱动开发者实现业务逻辑
return 0;
}
int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
{
if (deviceObject == NULL) {
return HDF_FAILURE;
}
static struct ISampleDriverService testService = {
.ServiceA = SampleDriverServiceA,
.ServiceB = SampleDriverServiceB,
};
deviceObject->service = &testService.ioService;
return HDF_SUCCESS;
}
#endif
// 驱动自身业务初始的接口。
int HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
{
if (deviceObject == NULL) {
HDF_LOGE("%s::ptr is null!", __func__);
return HDF_FAILURE;
}
HDF_LOGE("Sample driver Init success");
return HDF_SUCCESS;
}
// 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量。
struct HdfDriverEntry g_sampleDriverEntry = {
.moduleVersion = 1,
.moduleName = "sample_driver", // 驱动的关键名称。
.Bind = HdfSampleDriverBind,
.Init = HdfSampleDriverInit,
.Release = HdfSampleDriverRelease,
};
// 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,
// 再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
HDF_INIT(g_sampleDriverEntry);
2.2 创建Kconfig
$ cd ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample
$ touch Kconfig && vi Kconfig
# Copyright (c) 2020 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.
config DRIVERS_HDF_PLATFORM_HDF_SAMPLE
bool "Enable HDF platform HDF sample driver"
default n
depends on DRIVERS_HDF_PLATFORM
help
Answer Y to enable HDF platform HDF sample driver.
修改上层Kconfig
$ vi ~/harmony/sdk/vendor/huawei/hdf/Kconfig
source "../../vendor/huawei/hdf/display/driver/Kconfig"
+source "../../vendor/huawei/hdf/sample/platform/hdf-sample/Kconfig"
2.3 创建Makefile
$ cd ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample
$ touch Makefile && vi Makefile
# Copyright (c) 2020 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.
include $(LITEOSTOPDIR)/config.mk
include $(LITEOSTOPDIR)/../../drivers/hdf/lite/lite.mk
MODULE_NAME := hdf_sample
LOCAL_CFLAGS += $(HDF_INCLUDE)
LOCAL_SRCS += hdf_sample.c
# 警告需要关掉,不然unused的变量都会报错!
#LOCAL_CFLAGS += -fstack-protector-strong -Wextra -Wall -Werror
LOCAL_CFLAGS += -fstack-protector-strong
include $(HDF_DRIVER)
2.4 将驱动模块加入编译
$ vi ~/harmony/sdk/vendor/huawei/hdf/hdf_vendor.mk
LITEOS_BASELIB += -lhdf_uart_sample
LIB_SUBDIRS += $(VENDOR_HDF_DRIVERS_ROOT)/sample/platform/uart
+LITEOS_BASELIB += -lhdf_sample
+LIB_SUBDIRS += $(VENDOR_HDF_DRIVERS_ROOT)/sample/platform/hdf-sample
LITEOS_BASELIB中的hdf_sample为Makefile里的MODULE_NAME名字。
2.5 Makefile格式说明
驱动代码的编译必须要使用HDF框架提供的Makefile模板进行编译。
include $(LITEOSTOPDIR)/../../drivers/hdf/lite/lite.mk #导入hdf预定义内容,必需
MODULE_NAME := #生成的结果文件
LOCAL_INCLUDE := #本驱动的头文件目录
LOCAL_SRCS := #本驱动的源代码文件
LOCAL_CFLAGS := #自定义的编译选项
include $(HDF_DRIVER) #导入模板makefile完成编译
编译结果文件链接到内核镜像,添加到vendor目录下的hdf_vendor.mk里面,示例如下:
LITEOS_BASELIB += -lxxx #链接生成的静态库
LIB_SUBDIRS += #驱动代码Makefile的目录
2.6 驱动编译
2.6.1 编译报错问题
1:kernel deconfig报错,需要修改。
$ python build.py ipcamera_hi3516dv300 -b debug
...
clean hi3516dv300 finish
make[1]: Entering directory '/home/ubuntu/harmony/sdk/kernel/liteos_a'
/home/ubuntu/harmony/sdk/kernel/liteos_a/tools/menuconfig/conf --silentoldconfig /home/ubuntu/harmony/sdk/kernel/liteos_a/Kconfig
*
* Restart config...
*
*
* Driver
*
Enable Driver (DRIVERS) [Y/n/?] y
Enable USB (DRIVERS_USB) [Y/n/?] y
Enable USB HCD (DRIVERS_USB_HOST_DRIVER) [Y/n/?] y
USB HCD
1. Enable EHCI HCD (USB 2.0) (DRIVERS_USB_HOST_EHCI)
> 2. Enable XHCI HCD (USB 3.0) (DRIVERS_USB_HOST_XHCI)
choice[1-2?]: 2
Enable USB Device Class Drivers (DRIVERS_USB_DEVICE_CLASS_DRIVERS) [Y/n/?] y
Enable HDF manager (DRIVERS_HDF) [Y/n/?] y
Enable HDF platform driver (DRIVERS_HDF_PLATFORM) [Y/n/?] y
Enable HDF platform i2c driver (DRIVERS_HDF_PLATFORM_I2C) [Y/n/?] y
Enable HDF platform spi driver (DRIVERS_HDF_PLATFORM_SPI) [Y/n/?] y
Enable HDF platform gpio driver (DRIVERS_HDF_PLATFORM_GPIO) [Y/n/?] y
Enable HDF platform watchdog driver (DRIVERS_HDF_PLATFORM_WATCHDOG) [Y/n/?] y
Enable HDF platform sdio driver (DRIVERS_HDF_PLATFORM_SDIO) [Y/n/?] y
Enable HDF platform rtc driver (DRIVERS_HDF_PLATFORM_RTC) [Y/n/?] y
Enable HDF hisi sdk driver (DRIVERS_HDF_PLATFORM_HISI_SDK) [Y/n/?] y
Enable HDF WiFi Host driver (DRIVERS_HDF_WIFI) [Y/n/?] y
Enable Hi3881 Host driver (DRIVERS_HI3881) [Y/n/?] y
Enable HDF input driver (DRIVERS_HDF_INPUT) [Y/n/?] y
Enable HDF tp 5P5 gt911 driver (DRIVERS_HDF_TP_5P5_GT911) [Y/n/?] y
Enable HDF Lcd driver (DRIVERS_HDF_LCD) [Y/n/?] y
Enable HDF Lcd Icn9700 driver (DRIVERS_HDF_LCD_ICN9700) [Y/n/?] y
Enable HDF platform HDF sample driver (DRIVERS_HDF_PLATFORM_HDF_SAMPLE) [N/y/?] (NEW) aborted!
Console input/output is redirected. Run 'make oldconfig' to update configuration.
make[1]: *** [Makefile:129: genconfig] Error 1
处理:
通过搜索定位DRIVERS_HDF_LCD_ICN9700 的配置,然后参考其用法。
$ grep DRIVERS_HDF_LCD_ICN9700 . -nr
./kernel/liteos_a/.config:159:LOSCFG_DRIVERS_HDF_LCD_ICN9700=y
./vendor/huawei/hdf/display/driver/Kconfig:8:config DRIVERS_HDF_LCD_ICN9700
查看该目录下的一个编译脚本build.sh:
$ vi ~/harmony/sdk/kernel/liteos_a/build.sh
发现在脚本里面将hi3516dv300_release.config拷贝为了.config:
$ vi ~/harmony/sdk/kernel/liteos_a/tools/build/config/hi3516dv300_release.config
查看配置:
$ vi ~/harmony/sdk/kernel/liteos_a/.config
尝试手动添加DRIVERS_HDF_PLATFORM_HDF_SAMPLE为y:
$ vi ~/harmony/sdk/kernel/liteos_a/tools/build/config/hi3516dv300_clang.config
LOSCFG_DRIVERS_HDF_LCD_ICN9700=y
+LOSCFG_DRIVERS_HDF_PLATFORM_HDF_SAMPLE=y
LOSCFG_DRIVERS_HDF_USB=y
让修改生效:
$ cd ~/harmony/sdk/kernel/liteos_a/
$ ./build.sh hi3516dv300 clang debug
sh param:hi3516dv300,clang,debug
然后重新编译,该问题解决通过。
2.6.2 编译
报错问题2:变量类型不匹配
hdf_sample.c:53:21:
error: incompatible pointer types initializing
'int32_t (*)(struct HdfDeviceIoClient *, int, struct HdfSBuf *, struct HdfSBuf *)'
(aka 'int (*)(struct HdfDeviceIoClient *, int, struct HdfSBuf *, struct HdfSBuf *)')
with an expression of type
'int (struct HdfDeviceObject *, int, struct HdfSBuf *, struct HdfSBuf *)'
[-Werror,-Wincompatible-pointer-types]
.Dispatch = HdfSampleDriverDispatch,
处理:
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample/hdf_sample.c
// Dispatch是驱动中用来处理用户态发下来的消息的函数。
-int32_t HdfSampleDriverDispatch( struct HdfDeviceObject *deviceObject, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
+int32_t HdfSampleDriverDispatch(struct HdfDeviceIoClient *deviceIoClient, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
{
HDF_LOGE("%s: received cmd %d", __func__, id);
// 通HdfDeviceSendEvent函数触发用户空间获取驱动上报数据。
- return HdfDeviceSendEvent(deviceObject, id, data);
+ return HdfDeviceSendEvent(deviceIoClient->device, id, data);
}
return HDF_FAILURE;
}
再次编译,该问题解决通过。
查看驱动模块hdf_sample存放位置:
~/harmony/sdk$ find -name hdf_sample
./out/ipcamera_hi3516dv300/obj/kernel/liteos_a/obj/vendor/huawei/hdf/sample/platform/hdf_sample
3. 编写用户程序和驱动交互代码
3.1 基于HDF框架编写的用户态程序和驱动交互的代码如下:
$ mkdir -p ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample/app
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample/app/hdf_sample_app.c
#include <fcntl.h>
#include <unistd.h>
#include "hdf_log.h"
#include "hdf_sbuf.h"
#include "hdf_io_service_if.h"
#define HDF_LOG_TAG "sample_test"
#define SAMPLE_SERVICE_NAME "sample_service" // 服务的关键名称。
#define SAMPLE_WRITE_READ 123
#define SELECT_DEFAULT 0
int g_replyFlag = 0;
// 用户空间回调函数,驱动通过HdfDeviceSendEvent发送事件后,该函数将处理返回数据。
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_LOGE("user space received:");
HDF_LOGE("%s: dev event received: %u %s", (char *)priv, id, string);
g_replyFlag = 1;
return HDF_SUCCESS;
}
// 用户空间给内核驱动发送数据
static int SendEvent(struct HdfIoService *serv, char *eventData)
{
int ret = 0;
// 获取系统分配的data sbuf。
struct HdfSBuf *data = HdfSBufObtainDefaultSize();
if (data == NULL) {
HDF_LOGE("fail to obtain sbuf data");
return 1;
}
// 获取系统分配的reply sbuf。
struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
if (reply == NULL) {
HDF_LOGE("fail to obtain sbuf reply");
ret = HDF_DEV_ERR_NO_MEMORY;
goto out;
}
// 将用户空间目标数据放到sbuf。
if (!HdfSbufWriteString(data, eventData)) {
HDF_LOGE("fail to write sbuf");
ret = HDF_FAILURE;
goto out;
}
// 通过驱动服务提供的Dispatch实现用户态应用和内核态驱动的信息交互。
ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);
if (ret != HDF_SUCCESS) {
HDF_LOGE("fail to send service call");
goto out;
}
#if SELECT_DEFAULT
// 默认将sbuf中获取到的int数据放到用户空间变量中。
int replyData = 0;
if (!HdfSbufReadInt32(reply, &replyData)) {
HDF_LOGE("fail to get service call reply");
ret = HDF_ERR_INVALID_OBJECT;
goto out;
}
HDF_LOGE("Get reply is: %d", replyData);
#else
// 将sbuf中获取到的字符串数据放到用户空间变量中。
const char *replyData = HdfSbufReadString(reply);
if (replyData == NULL) {
HDF_LOGE("fail to get service call reply");
ret = HDF_ERR_INVALID_OBJECT;
goto out;
}
HDF_LOGE("Get reply is: %s", replyData);
#endif
out:
HdfSBufRecycle(data); // 释放用于数据中转的sbuf
HdfSBufRecycle(reply); // 释放用于数据中转的sbuf
return ret;
}
int main()
{
char *sendData = "come from user sapce: default event info";
// 用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息。
struct HdfIoService *serv = HdfIoServiceBind(SAMPLE_SERVICE_NAME, 0);
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 0
// 补充:
// 驱动服务的获取有两种方式,HDF框架提供接口直接获取和HDF框架提供订阅机制获取。
// 1. 当明确驱动已经加载完成时,获取该驱动的服务可以通过HDF框架提供的能力接口直接获取,如下所示:
const struct ISampleDriverService *sampleService =
(const struct ISampleDriverService *)DevSvcManagerClntGetService("sample_driver");
if (sampleService == NULL) {
return -1;
}
sampleService->ServiceA();
sampleService->ServiceB(5);
// 2. 当对驱动(同一个host)加载的时机不感知时,可以通过HDF框架提供的订阅机制来订阅该驱动。
#endif
// 通过HDF提供的订阅机制获取驱动服务。
if (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS) {
HDF_LOGE("fail to register event listener");
return HDF_FAILURE;
}
// 用户空间给内核驱动发送数据
HDF_LOGE("user space send:");
HDF_LOGE("cmd: %u, send data is: %s", SAMPLE_WRITE_READ, sendData);
if (SendEvent(serv, sendData)) {
HDF_LOGE("fail to send event");
return HDF_FAILURE;
}
/* wait for event receive event finishing */
while (g_replyFlag == 0) {
sleep(1);
}
// 用户态程序注册接收驱动上报事件的操作方法。
if (HdfDeviceUnregisterEventListener(serv, &listener)) {
HDF_LOGE("fail to unregister listener");
return HDF_FAILURE;
}
// 释放驱动服务。
HdfIoServiceRecycle(serv);
return HDF_SUCCESS;
}
说明: 用户态应用程序使用了HDF框架中的消息发送接口,因此在编译用户态程序的过程中需要依赖HDF框架对外提供的hdf_core和osal的动态库,在gn编译文件中添加如下依赖项:
deps = [ "//drivers/hdf/lite/manager:hdf_core", "//drivers/hdf/lite/adapter/osal/posix:hdf_posix_osal", ]
3.2 编写编译APP源码的BUILD.gn文件
$ vi ~/harmony/sdk/vendor/huawei/hdf/sample/platform/hdf-sample/BUILD.gn
# Copyright (c) 2020 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/lite/config/component/lite_component.gni")
import("//build/lite/ndk/ndk.gni")
HDF_FRAMEWORKS = "//drivers/hdf/frameworks"
executable("hdf_sample_app") {
sources = [
"hdf_sample_app.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",
"//third_party/bounds_checking_function/include",
]
deps = [
"//drivers/hdf/lite/manager:hdf_core",
"//drivers/hdf/lite/adapter/osal/posix:hdf_posix_osal",
]
public_deps = [
"//third_party/bounds_checking_function:libsec_shared",
]
defines = [
"__USER__",
]
cflags = [
"-Wall",
"-Wextra",
]
}
注意,里面的对齐不能使用table键,否则编译会报错。
3.3 将hdf_sample_app.c加入编译
$ vi ~/harmony/sdk/build/lite/product/ipcamera_hi3516dv300.json
{
"subsystem": [
{
"name": "hdf",
"component": [
{ "name": "posix", "dir": "//drivers/hdf/lite/posix:hdf_posix", "features":[] },
{ "name": "manager", "dir": "//drivers/hdf/lite/manager:hdf_manager", "features":[] },
{ "name": "wifi", "dir": "//vendor/huawei/hdf/wifi:wifi_firmware", "features":[] },
{ "name": "display", "dir": "//vendor/huawei/hdf/display/hdi:hdi_display", "features":[] },
{ "name": "input", "dir": "//vendor/huawei/hdf/input/hdi:hdi_input", "features":[] },
{ "name": "hdf_sample", "dir": "//vendor/huawei/hdf/sample/platform/uart:hello_uart_sample", "features":[] },
+ { "name": "hdf_sample_app", "dir": "//vendor/huawei/hdf/sample/platform/hdf-sample/app:hdf_sample_app", "features":[] }
]
},
]
}
3.4 应用编译
$ python build.py ipcamera_hi3516dv300 -b debug
$ vi out/ipcamera_hi3516dv300/build.log
Done. Made 251 targets from 160 files in 907ms
ninja: Entering directory `/home/ubuntu/harmony/sdk/out/ipcamera_hi3516dv300'
[1/1346] STAMP obj/applications/sample/camera/communication/sample.stamp
...
// 新增的hdf_sample_app
[1163/1346] clang obj/vendor/huawei/hdf/sample/platform/hdf-sample/app/hdf_sample_app.o
[1167/1346] LLVM LINK ./bin/hdf_sample_app
// 可供参考的hello_uart
[1160/1346] clang obj/vendor/huawei/hdf/sample/platform/uart/dev/hello_uart_dev.o
[1161/1346] LLVM LINK ./bin/hello_uart
[1169/1346] clang obj/vendor/huawei/hdf/sample/platform/uart/dispatch/uart_if.o
[1170/1346] clang obj/vendor/huawei/hdf/sample/platform/uart/dispatch/hello_uart_dispatch.o
[1171/1346] LLVM LINK ./bin/hello_uart_dispatch
[1172/1346] STAMP obj/vendor/huawei/hdf/sample/platform/uart/hello_uart_sample.stamp
...
[1344/1346] STAMP obj/build/lite/ohos.stamp
[1345/1346] ACTION //build/lite:gen_rootfs(//build/lite/toolchain:linux_x86_64_clang)
[1346/1346] STAMP obj/build/lite/gen_rootfs.stamp
ohos ipcamera_hi3516dv300 build success!
通过上面的编译日志可以找到hdf_sample_app应用放在了/bin目录:
./bin/hdf_sample_app
4. 烧录
4.1 将系统bin文件拷贝到Windows共享目录中:
$ rm /mnt/hgfs/proj-harmony/images/out/ -rf
$ cp -rf out/ /mnt/hgfs/proj-harmony/images/
4.2 在HiTool工具中依次填入OHOS_Image.bin、rootfs.img、userfs.img的文件位置
操作流程如下:
修改一下长度:
点击“烧写”按键后,控制台会提示重启目标板,重启后,系统就自动进入烧写了。
单板初次启动需要修改启动参数,重新上电后登陆会进入uboot中,如果分区位置没有变化则不用执行下面修改启动参数的指令。
hisilicon # setenv bootcmd "mmc read 0x0 0x80000000 0x800 0x3000; go 0x80000000";
hisilicon # setenv bootargs "console=ttyAMA0,115200n8 root=emmc fstype=vfat rootaddr=7M rootsize=15M rw";
hisilicon # saveenv
hisilicon # reset
重启后进入系统。
注释:表示设置启动参数,输出模式为串口输出,波特率为115200,数据位8,rootfs挂载于emmc器件,文件系统类型为vfat,“rootaddr=6M rootsize=14M rw”处对应填入rootfs.img的烧写起始位置与长度,此处与新增rootfs.img文件时所填大小必须相同。
5. 测试
在根目录下,在命令行输入下面命令执行写入的demo程序,显示成功结果如下所示:
OHOS # ./bin/hdf_sample_app
// 用户空间给内核驱动发送数据
[HDF:E/"sample_test"]user space send:
[HDF:E/"sample_test"]cmd: 123, send data is: come from user sapce: default event info
// 被驱动中用来处理用户态发下来的消息的函数HdfSampleDriverDispatch接收到
[ERR][HDF:E/sample_driver]HdfSampleDriverDispatch: received cmd 123
[ERR][HDF:E/sample_driver]HdfSampleDriverDispatch: read data is: come from user sapce: default event info
// 默认:用户空间回调函数OnDevEventReceived的打印信息
[HDF:E/"sample_test"]user space received:
[HDF:E/"sample_test"]Service0: dev event received: 123 come from user sapce: default event info
[HDF:E/"sample_test"]Get reply is: 2147483647
[HDF:E/hdf_syscall_adapter]event listener task exit
// 修改后:
[HDF:E/"sample_test"]user space received:
[HDF:E/"sample_test"]Service0: dev event received: 123 come from user sapce: default event info // 这了尝试打印出“change default event info”,但修改无效
[HDF:E/"sample_test"]Get reply is: come from kernel sapce: default event info
[HDF:E/hdf_syscall_adapter]event listener task exit
打印结果的截图:
6. 吐槽:
1. 内核config的详细配置说明没有看到。
2. 编译报错位置定位麻烦,不会在编译出错的位置立马停下来,需要在编译日志中搜索关键字FAILED、failed、ERROR、error等。
3. 重新编译时未修改过的文件都要重新编译一遍;分区文件的内容大概是些什么也不清楚,如果只修改了驱动或文件系统,是否可以只烧写某一个分区;不知道在哪里设置多核并行编译等。
4. 驱动如何手动加载和卸载也无说明。
5. 发现华为对整个SDK框架介绍比较少,好在靠自己慢慢分析SDK结构、或者照着已有源码“依葫芦画瓢”勉强能解决大部分问题,不过这样走了很多弯路,希望华为能逐渐将关键细节都写到开发文档中,不用每个人都走一遍常见的问题。
7. 附件:
修改过后的diff文件:
ubuntu@ubuntu20:~/harmony/sdk$ git show > hdf_test.diff
本文结束,感谢您的阅读!