2020-12-06 21:32:57
0
参考文档
链接: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 ***uf", __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 ***uf", __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_***uf.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 ***uf。
- struct HdfSBuf *data = HdfSBufObtainDefaultSize();
- if (data == NULL) {
- HDF_LOGE("fail to obtain ***uf data");
- return 1;
- }
- // 获取系统分配的reply ***uf。
- struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
- if (reply == NULL) {
- HDF_LOGE("fail to obtain ***uf reply");
- ret = HDF_DEV_ERR_NO_MEMORY;
- goto out;
- }
- // 将用户空间目标数据放到***uf。
- if (!HdfSbufWriteString(data, eventData)) {
- HDF_LOGE("fail to write ***uf");
- 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
- // 默认将***uf中获取到的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
- // 将***uf中获取到的字符串数据放到用户空间变量中。
- 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); // 释放用于数据中转的***uf
- HdfSBufRecycle(reply); // 释放用于数据中转的***uf
- 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/***uf/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目录:
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
本文结束,感谢您的阅读!
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。
侵权投诉