IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 鸿蒙操作系统系列——Hi3516 OpenHarmony_release_v1.1.0 LTS版本led内核态驱动与用户态应用贯通篇 -> 正文阅读

[系统运维]鸿蒙操作系统系列——Hi3516 OpenHarmony_release_v1.1.0 LTS版本led内核态驱动与用户态应用贯通篇

Hi3516 OpenHarmony_release_v1.1.0 LTS版本led内核态驱动与用户态应用贯通篇

此文档是针对 OpenHarmony 2021年4月10日发布的OpenHarmony_release_v1.1.0 LTS 版本分析。

1. 开发环境

  1. HiSpark_AI_Hi3516D300开发板

  2. 虚拟机安装Ubuntu18.04版本,参考https://device.harmonyos.com/cn/docs/start/introduce/oem_minitinier_environment_lin-0000001105407498搭好建开发环境。

2. 开发目标

在HiSpark_AI_Hi3516D300开发板上实现红外LED灯的亮灭控制,支持内核态驱动程序和用户态应用程序双向通信,实现用户态发送一个控制红外灯亮灭的消息到内核态,同时内核态返回灯的状态消息发送到用户态程序。

3. 开发准备

1)板卡级编译配置文件:device/hisilicon/hispark_taurus/sdk_liteos/config.gni

2)hi3516dv300驱动闭源lib库的路径在目录:device/hisilicon/drivers/libs/ohos/llvm/hi3516dv300

3)Hi3516开发板的红外灯的 IO 口编号为GPIO5_1。

在这里插入图片描述

4. 内核态开发

4.1 HDF简介

HDF(OpenHarmony Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。

HDF框架以组件化的驱动模型作为核心设计思路,为开发者提供更精细化的驱动管理,让驱动开发和部署更加规范。HDF框架将一类设备驱动放在同一个host里面,驱动内部实现开发者也可以将驱动功能分层独立开发和部署,支持一个驱动多个node,HDF框架管理驱动模型如下图所示。

在这里插入图片描述

4.2 HDF实现

4.2.1 框架驱动实现

在vendor/huawei/hdf/sample/platform目录下新增led目录,新建如下文件夹和文件,其中include文件夹包含led_driver.h,src文件夹包含led_driver.c,led_driver.c文件实现HDF驱动框架代码。

主要代码如下。

#include "hdf_device_desc.h"    // HDF框架对驱动开放相关能力接口的头文件
#include "hdf_log.h"            // HDF框架提供的日志接口头文件
#include "device_resource_if.h" // HDF框架获取设备配置信息的接口头文件

//驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL)
    {
        HDF_LOGE("Led driver bind failed!");
        return HDF_ERR_INVALID_OBJECT;
    }
    static struct IDeviceIoService ledService = {
        .Dispatch = LedDriverDispatch,
    };
    deviceObject->service = (struct IDeviceIoService *)(&ledService);
    HDF_LOGD("Led driver bind success");
    return HDF_SUCCESS;
}

// 驱动自身业务初始的接口
int32_t HdfLedDriverInit(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL)
    {
        HDF_LOGE("Led driver Init failed!");
        return HDF_ERR_INVALID_OBJECT;
    }
    HDF_LOGD("Led driver Init success");
    return HDF_SUCCESS;
}

// 驱动资源释放的接口
void HdfLedDriverRelease(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL)
    {
        HDF_LOGE("Led driver release failed!");
        return;
    }

    HDF_LOGD("Led driver release success");
    return;
}

// 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量
struct HdfDriverEntry g_ledDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "HDF_LED",
    .Bind = HdfLedDriverBind,
    .Init = HdfLedDriverInit,
    .Release = HdfLedDriverRelease,
};

// 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
HDF_INIT(g_ledDriverEntry);

4.2.2 驱动编译

在vendor/huawei/hdf/sample/platform/led目录下新增Makefile文件。

include $(LITEOSTOPDIR)/config.mk
include $(LITEOSTOPDIR)/../../drivers/adapter/khdf/liteos/lite.mk

MODULE_NAME := hdf_led

LOCAL_CFLAGS += $(HDF_INCLUDE)

LOCAL_SRCS += src/led_driver.c \

LOCAL_INCLUDE := ./include
LOCAL_CFLAGS += -fstack-protector-strong

include $(HDF_DRIVER)

4.2.3 链接到内核镜像

修改device/hisilicon/drivers/lite.mk 文件新增以下内容。

ifeq ($(LOSCFG_DRIVERS_HDF_PLATFORM_LED), y)
        LITEOS_BASELIB += -lhdf_led
        LIB_SUBDIRS    += $(LITEOS_SOURCE_ROOT)/vendor/huawei/hdf/sample/platform/led
endif

4.2.4 驱动配置

驱动配置包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息。

(1)驱动设备配置

需要修改device/hisilicon/hispark_taurus/sdk_liteos/config/device_info/device_info.hcs文件,新增以下内容。其中,moduleName、serviceName和deviceMatchAttr 都比较重要,分布链接到源码的不同位置,我这里都分开命名,便于理解,设备级配置信息也需要同步修改,在文件vendor/hisilicon/hispark_taurus/config/device_info/device_info.hcs里面同步以下信息。

device_led :: device {              // led设备节点
    device0 :: deviceNode {         // led驱动的DeviceNode节点
    policy = 2;                     // policy字段是驱动服务发布的策略,在驱动服务管理章节有详细介绍
    priority = 40;                  // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序
    permission = 0644;              // 驱动创建设备节点权限
    moduleName = "HDF_LED";         // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
    serviceName = "led_service";    // 驱动对外发布服务的名称,必须唯一
    deviceMatchAttr = "led_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等
    }
}

(2)驱动私有配置信息

如果驱动有私有配置,可以添加一个驱动的配置文件,填写驱动的配置信息,HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject 的property里面传递给驱动。在device/hisilicon/hispark_taurus/sdk_liteos/config/led目录新增led_config.hcs文件,内容如下。

root{
    LedDriverConfig {
        led_version = 1;
        match_attr = "led_config";   //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
    }
}

4.2.5 板级配置

配置信息定义之后,需要将该配置文件添加到板级配置入口文件device/hisilicon/hispark_taurus/sdk_liteos/config/hdf.hcs。

#include "led/led_config.hcs"

4.3 驱动消息机制实现

当用户态应用程序和内核态驱动程序需要交互数据时,可以使用HDF框架的消息机制来实现。消息通信机制可以在用户态和内核态之间架起桥梁,这为我们之后的APP与底层驱动提供了双向通信的能力。本案例在用户态发送一个控制红外灯亮灭的消息到内核态,同时内核态返回灯的状态消息发送到用户态程序。

实现内核态的消息服务接口,编辑 led_driver.c, 实现服务基类成员IDeviceIoService中的Dispatch方法。收到用户态发来的命令后,操作红外LED设备,然后将返回值通过reply传回,最后将收到的命令回传给用户态程序。

// Dispatch是用来处理用户态发下来的消息
int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    int32_t result = HDF_FAILURE;

    if (client == NULL || client->device == NULL)
    {
        HDF_LOGE("Led driver device is NULL");
        return HDF_ERR_INVALID_OBJECT;
    }

    switch (cmdCode)
    {
	    case LED_WRITE_READ:
	    	//读取用户发过来的数据 
	        const char *recv = HdfSbufReadString(data);
	        if (recv != NULL)
	        {
	            HDF_LOGI("recv: %s", recv);
				if (strcmp(recv, "on") == 0)
				{
					LedControl(LED_LIGHT_ON);
					result = 1;
				}
				else if (strcmp(recv, "off") == 0)
				{
					LedControl(LED_LIGHT_OFF);
					result = 0;
				}
				else
				{
					HDF_LOGE("recv: %s invalid led mode", recv);
					result = -1;
				}
				
				//返回值通过reply回传
	            if (!HdfSbufWriteInt32(reply, result))
	            {
	                HDF_LOGE("replay is fail");
	            }
	            
	            //返回接收的数据发回给用户程序
	            return HdfDeviceSendEvent(client->device, cmdCode, data);
	        }
	        break;

	    default:
	        break;
    }
	
    return result;
}

修改 HdfLedDriverBind函数,将服务绑定到框架。

//驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL)
    {
        HDF_LOGE("Led driver bind failed!");
        return HDF_ERR_INVALID_OBJECT;
    }
    static struct IDeviceIoService ledService = {
        .Dispatch = LedDriverDispatch,
    };
    deviceObject->service = (struct IDeviceIoService *)(&ledService);
    HDF_LOGD("Led driver bind success");
    return HDF_SUCCESS;
}

4.4 业务代码实现

编辑 led_driver.c,实现业务逻辑控制代码。通过mode参数实现LED灯的亮灭功能。其中Hi3516DV300的控制器管理12组GPIO管脚,每组8个。GPIO号 = GPIO组索引(0~11)* 每组GPIO管脚数(8) + 组内偏移,GPIO5_1的GPIO号 = 5 * 8 +1 = 41。

static int32_t LedControl(int mode)
{
    int32_t ret;

    /* LED的GPIO管脚号 */
    uint16_t gpio = 5 * 8 + 1;  // 红外补光灯

    /* 将GPIO管脚配置为输出 */
    ret = GpioSetDir(gpio, GPIO_DIR_OUT);
    if (ret != 0)
    {
        HDF_LOGE("GpioSetDir: failed, ret %d\n", ret);
        return ret;
    }

	//点亮LED
    if (mode == LED_LIGHT_ON)
    {
        ret = GpioWrite(gpio, GPIO_VAL_HIGH);
    }
    else if (mode == LED_LIGHT_OFF) //关闭LED
    {
        ret = GpioWrite(gpio, GPIO_VAL_LOW);
    }
	else
    {
    	HDF_LOGE("LedControl: invalid led mode.\n");
		ret = -1;
	}
    	
    if (ret != 0)
    {
        HDF_LOGE("LedControl: failed, ret = %d\n", ret);
        return ret;
    }
	
    return ret;
}

4.5 配置KConfig

drivers/adapter/khdf/liteos/Kconfig添加LED相关的配置项控制编译。

config DRIVERS_HDF_PLATFORM_LED
    bool "Enable HDF platform led driver"
        default n
    depends on DRIVERS_HDF_PLATFORM
    help
      Answer Y to enable HDF platform led driver.

在kernel/liteos_a目录下输入make menuconfig配置KConfig文件,选择Driver->Enable HDF platform led driver选项。
在这里插入图片描述

系统编译配置文件:kernel/liteos_a/tools/build/config/hispark_taurus_clang.config,LiteOS内核对配置进行修改后需要将内核配置的.config文件拷贝到上述路径下,命令如下:

cp .config  tools/build/config/debug/hispark_taurus_clang.config

因为hb build -f每次会从kernel/liteos_a/tools/build/config/hispark_taurus_clang.config拷贝一份默认的配置到kernel/litos_a目录下把之前的配置给覆盖掉。

5. 用户态开发

我们开始写个led测试程序通过消息机制来与内核态交互,新建applications/sample/camera/ledApp/my_led_app.c源文件。代码如下:

//设置回调函数,收到内核态发来的消息,打印信息
static int OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *data)
{
    const char *string = HdfSbufReadString(data);
    if (string == NULL)
    {
        printf("fail to read string in event data\n");
        return HDF_FAILURE;
    }
    printf("%s received driver data: led %s\n", (char *)priv, string);

    return HDF_SUCCESS;
}

//先实现一个发送消息的函数SendEvent,发送字符串命令后,收回内核态reply中操作设备后的返回值,放入replyData中打印出来,操作成功返回0。
static int SendEvent(struct HdfIoService *serv, char *eventData)
{
    int ret = 0;
    struct HdfSBuf *data = HdfSBufObtainDefaultSize();
    if (data == NULL)
    {
        printf("fail to obtain sbuf data");
        return 1;
    }

    struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
    if (reply == NULL)
    {
        printf("fail to obtain sbuf reply\n");
        ret = HDF_DEV_ERR_NO_MEMORY;
        goto out;
    }

    if (!HdfSbufWriteString(data, eventData))
    {
        printf("fail to write sbuf");
        ret = HDF_FAILURE;
        goto out;
    }

    ret = serv->dispatcher->Dispatch(&serv->object, LED_WRITE_READ, data, reply);
    if (ret != HDF_SUCCESS)
    {
        printf("fail to send service call\n");
        goto out;
    }

    int replyData = 0;
    if (!HdfSbufReadInt32(reply, &replyData))
    {
        printf("fail to get service call reply\n");
        ret = HDF_ERR_INVALID_OBJECT;
        goto out;
    }
    printf("Get reply is: %d\n", replyData);
out:
    HdfSBufRecycle(data);
    HdfSBufRecycle(reply);
    return ret;
}

//先获取led_service服务,通过服务名称,绑定到对应的驱动。然后设置监听,等待来自内核的消息。用户通过led on/off来控制红外LED灯的亮灭。
int main(int argc, char **argv)
{
	if (argc < 2)
	{
		printf("./ledApp [on/off]");
		return HDF_FAILURE;	
	}

    struct HdfIoService *serv = HdfIoServiceBind(LED_SERVICE);
    if (serv == NULL)
    {
        printf("fail to get service %s\n", LED_SERVICE);
        return HDF_FAILURE;
    }

    static struct HdfDevEventlistener listener = {
        .callBack = OnDevEventReceived,
        .priv = "ledApp"
    };

    if (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS)
    {
        printf("fail to register event listener\n");
        return HDF_FAILURE;
    }

    SendEvent(serv, argv[1]);

	sleep(2);  //延时2秒等待回应

    if (HdfDeviceUnregisterEventListener(serv, &listener))
    {
        printf("fail to  unregister listener\n");
        return HDF_FAILURE;
    }

    HdfIoServiceRecycle(serv);
    printf("exit!\n");

    return HDF_SUCCESS;
}

新建applications/sample/camera/ledApp/BUILD.gn,配置BUILD.gn

import("//build/lite/config/component/lite_component.gni")

executable("ledApp") {
  sources = [ "my_led_app.c" ]
  cflags = [ "-Wall" ]
  cflags_cc = cflags
  include_dirs = [
    "//drivers/framework/include/utils",
    "//drivers/adapter/khdf/liteos/osal/include",
    "//drivers/framework/ability/sbuf/include", 
    "//drivers/framework/core/shared/include",
    "//drivers/framework/core/host/include",
    "//drivers/framework/core/manager/include",
    "//drivers/framework/include/core",
    "//drivers/framework/include/utils",
    "//drivers/framework/utils/include",
    "//drivers/framework/include/osal",
  ]

  ldflags = [ "-lstdc++" ]
  ldflags += [ "-lpthread" ]
  ldflags += [ "-Wl,-rpath-link=$ohos_root_path/$root_out_dir" ]

  deps = [
    "//drivers/adapter/uhdf/manager:hdf_core",
    "//drivers/adapter/uhdf/posix:hdf_posix_osal",
  ]
  output_dir = "$root_out_dir/"
}

lite_component("led_app") {
  features = [
    ":ledApp",
  ]
}

在build/lite/components/applications.json文件新增如下内容。

{
    "component": "led_app",
    "description": "Led related samples.",
    "optional": "true",
    "dirs": [
    "applications/sample/camera/ledApp"
    ],
    "targets": [
    "//applications/sample/camera/ledApp:ledApp"
    ],
    "rom": "",
    "ram": "",
    "output": [],
    "adapted_kernel": [ "liteos_a" ],
    "features": [],
    "deps": {
    "components": [],
    "third_party": []
    }
},

6. 系统编译烧写

在系统根目录下执行hb build -f进行系统编译。

系统烧写可以采用TFTP简化烧写过程。

//更新内核
tftp 0x81000000 OHOS_Image.bin
mmc write 0x0 0x81000000 0x800 0x36c8

//更新根文件系统
tftp 0x81000000 rootfs_vfat.img
mmc write 0x0 0x81000000 0x5000 0x7fae

//更新用户数据空间
tftp 0x81000000 userfs_vfat.img
mmc write 0x0 0x81000000 0xf000 0x19000

7. 测试

测试结果符合设计目标。

OHOS # ./ledApp on
OHOS # Get reply is: 1
01-01 00:33:38.741 17 80 I 02500/led_driver: recv: on
ledApp received driver data: led on
exit!
01-01 00:33:40.742 17 81 I 02500/hdf_syscall_adapter: event listener task received exit event
01-01 00:33:40.742 17 81 I 02500/hdf_syscall_adapter: event listener task exit
      
OHOS # 
OHOS # 
OHOS # ./ledApp off
OHOS # Get reply is: 0
01-01 00:33:49.654 18 80 I 02500/led_driver: recv: off
ledApp received driver data: led off
exit!
01-01 00:33:51.654 18 81 I 02500/hdf_syscall_adapter: event listener task received exit event
01-01 00:33:51.654 18 81 I 02500/hdf_syscall_adapter: event listener task exit

8. 总结

驱动和应用开发涉及到文件和配置比较多,关系也比较纷繁,而且分散在各个目录,这里列出主要文件再梳理一下:

//应用主目录                                用户态
applications
|--sample
     |--camera
          |--ledApp
                |--BUILD.gn
                |--my_ledapp.c   //用户态主程序
//产品级主目录                                内核态
vender
|--huawei
     |--hdf
         |--sample
               |--platform
                      |--led
                           |--Makefile
                           |--src
                               |--led_driver.c 驱动实现文件
                           |--include
                               |--led_driver.h
//板卡级配置文件
drivers/adapter/khdf/liteos/Kconfig
device/hisilicon/drivers/lite.mk
device/hisilicon/hispark_taurus/sdk_liteos/config/device_info/device_info.hcs
device/hisilicon/hispark_taurus/sdk_liteos/config/hdf.hcs
device/hisilicon/hispark_taurus/sdk_liteos/config/led/led_config.hcs
//产品级配置文件
vendor/hisilicon/hispark_taurus/config.json
vendor/hisilicon/hispark_taurus/config/device_info/device_info.hcs
vendor/hisilicon/hispark_taurus/config/hdf.hcs
vendor/hisilicon/hispark_taurus/config/led/led_config.hcs
//应用级配置文件
build/lite/components/applications.json

大致上分三个部分:内核态、用户态和配置文件。

(1) 内核态
首先由led_driver.c生成名为led_service的服务,以g_ledDriverEntry结构注册到HCS框架。编译成hdf_led_driver驱动,通过device/hisilicon/drivers/lite.mk链接到内核镜像中。通过 HdfLedDriverBind函数将led_driver模块绑定到HDF框架,IDeviceIoService中的Dispatch方法来处理来自用户态消息。

(2) 用户态
用户态以HdfIoServiceBind通过服务名led_service来找到相应的驱动,用HdfSbufWriteString来发送消息,serv->dispatcher->Dispatch来接收返回值,通过HdfDeviceRegisterEventListener设置监听,来获取内核态主动发送的消息。

(3)驱动配置
以模块名led_driver找到注册到HCS框架的驱动程序,以服务名led_service暴露给用户态程序或内核态程序调用,以led_config链接驱动私有配置文件,最后通过配置文件来进行耦合,保证了灵活性。这种设计在分布式的场合中,会有比较大的便利性。
cs
//应用级配置文件
build/lite/components/applications.json


大致上分三个部分:内核态、用户态和配置文件。

**(1)  内核态**
首先由led_driver.c生成名为led_service的服务,以g_ledDriverEntry结构注册到HCS框架。编译成hdf_led_driver驱动,通过device/hisilicon/drivers/lite.mk链接到内核镜像中。通过  HdfLedDriverBind函数将led_driver模块绑定到HDF框架,IDeviceIoService中的Dispatch方法来处理来自用户态消息。

 

**(2)  用户态**
用户态以HdfIoServiceBind通过服务名led_service来找到相应的驱动,用HdfSbufWriteString来发送消息,serv->dispatcher->Dispatch来接收返回值,通过HdfDeviceRegisterEventListener设置监听,来获取内核态主动发送的消息。

 

**(3)驱动配置**
以模块名led_driver找到注册到HCS框架的驱动程序,以服务名led_service暴露给用户态程序或内核态程序调用,以led_config链接驱动私有配置文件,最后通过配置文件来进行耦合,保证了灵活性。这种设计在分布式的场合中,会有比较大的便利性。
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-07-24 11:53:55  更:2021-07-24 11:55:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 16:38:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码