HDF驱动开发流程解析

系统教程2年前发布 yuezh
24 0 0
文章目录
  • 驱动实现包含驱动业务代码实现和驱动入口注册,具体写法如下: #include "hdf_device_desc.h" // HDF框架对驱动开发相关能力接口的头文件#include "hdf_log.h" // HDF框架提供的日志接口头文件#define HDF_LOG_TAG "sample_driver" // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签。// 驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架。int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject){ HDF_LOGD("Sample driver bind success"); return 0;}// 驱动自身业务初始化的接口int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject){ HDF_LOGD("Sample driver Init success"); return 0;}// 驱动资源释放的接口void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject){ HDF_LOGD("Sample driver release success"); return;} 驱动入口注册到HDF框架。 // 定义驱动入口的对象,必须为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); 通过工具生成的驱动代码的位置位于drivers/framework/model里,我们通过Deveco Device Tool生成新增了LiteOS的测试驱动(hello_liteos)以及Linux内核的测试驱动(hello_linux)。 自动生成驱动业务代码以及将驱动入口注册到HDF框架的模板,为区别后续两种内核的驱动实现结果,我修改了一下驱动的初始化化Init函数,Liteos内核驱动实现打印"Hello LiteOS"的日志,而Linux内核的驱动实现打印“Hello Linux”的日志,不过实际测试结果Liteos内核的Hilog打印出了点问题,显示缓冲区满了,尝试增大缓冲区解决无果,所以加了Liteos内核驱动实现让绿色LED等常亮,Linux内核的驱动实现让红外灯常亮来区别。 通过查询原理图可以得: 绿色LED为GPIO2_3引脚,换算成管脚号为2×8+3=19。(Hi3516一组GPIO含有8个引脚),输出高电平点亮。 红外LED为GPIO为GPIO5_1引脚,换算成管脚号为5×8+1=41,输出高电平点亮。 这样就可以修改我们的驱动代码,注意需要包含#include "gpio_if.h"头文件用于控制GPIO,修改代码如下。 // Initialize the driver service.static int32_t HdfHelloLiteosDriverInit(struct HdfDeviceObject *deviceObject) { HDF_LOGI("Hello LiteOS"); GpioSetDir(19,GPIO_DIR_OUT);//引脚设置为输出 GpioWrite(19,GPIO_VAL_HIGH);//输出高电平,绿色LED亮 return 0;} // Initialize the driver service.static int32_t HdfHelloLinuxDriverInit(struct HdfDeviceObject *deviceObject) { HDF_LOGI("Hello Linux"); GpioSetDir(41,GPIO_DIR_OUT);//引脚设置为输出 GpioWrite(41,GPIO_VAL_HIGH);//输出高电平,红外灯亮 return 0;}
  • 在创建完成驱动代码后,还要把它加入OpenHarmony的构建系统,LiteOS内核与Linux内核的编译配置流程会有所不同。
  • 对两个内核的工程分别进行编译烧录运行。 结果如下: Linux内核: LiteOS内核: ​​想了解更多关于开源的内容,请访问:​​ ​​51CTO 开源基础软件社区​​ ​​https://ost.51cto.com​​。
  • HDF驱动开发流程解析

    ​想了解更多关于开源的内容,请访问:​

    ​51CTO 开源基础软件社区​

    ​https://ost.51cto.com​

    上一节我们通过DevEco Device Tool生成标准HDF框架,下面我们就以生成的模板,来深入研究HDF驱动开发。本次使用的是Hi3516DV300开发板,分别设置了LiteOS内核以及Linux内核两个版本为例,来了解深入HDF驱动开发流程以及比较Linux内核与LiteOS内核HDF驱动开发异同。

    HDF驱动开发流程解析

    HDF的开发流程可以参考下面这篇文章。

    驱动开发

    实例代码通过DevEco Device Tool生成,代码以及结构比较规范,可以参考下面这篇。

    DevEco Device Tool:HDF框架一键生成!

    本次测试例子使用的是OpenHarmony3.1 release全量代码下配置的两个工程,一个是LiteOS内核的小型系统,另一个是Linux内核的小型系统,并且参考上一篇文章生成HDF框架。

    HDF驱动开发流程解析

    HDF驱动开发流程解析

    生成的文件包括BUILD.gn, Kconfig, MakeFile, hcs, c/c++的编译,驱动代码等,点击即可跳转到对应的文件。Linux内核和LiteOS内核生成的会有点区别,主要体现在驱动编译方面。

    基于HDF框架的驱动开发主要分为三个部分:驱动实现、驱动编译和驱动配置。

    驱动实现包含驱动业务代码实现和驱动入口注册,具体写法如下:

    #include "hdf_device_desc.h" // HDF框架对驱动开发相关能力接口的头文件
    #include "hdf_log.h" // HDF框架提供的日志接口头文件
    #define HDF_LOG_TAG "sample_driver" // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签。
    // 驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架。
    int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
    {
    HDF_LOGD("Sample driver bind success");
    return 0;
    }
    // 驱动自身业务初始化的接口
    int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
    {
    HDF_LOGD("Sample driver Init success");
    return 0;
    }
    // 驱动资源释放的接口
    void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
    {
    HDF_LOGD("Sample driver release success");
    return;
    }

    驱动入口注册到HDF框架。

    // 定义驱动入口的对象,必须为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);

    通过工具生成的驱动代码的位置位于drivers/framework/model里,我们通过Deveco Device Tool生成新增了LiteOS的测试驱动(hello_liteos)以及Linux内核的测试驱动(hello_linux)。

    HDF驱动开发流程解析

    自动生成驱动业务代码以及将驱动入口注册到HDF框架的模板,为区别后续两种内核的驱动实现结果,我修改了一下驱动的初始化化Init函数,Liteos内核驱动实现打印"Hello LiteOS"的日志,而Linux内核的驱动实现打印“Hello Linux”的日志,不过实际测试结果Liteos内核的Hilog打印出了点问题,显示缓冲区满了,尝试增大缓冲区解决无果,所以加了Liteos内核驱动实现让绿色LED等常亮,Linux内核的驱动实现让红外灯常亮来区别。

    HDF驱动开发流程解析

    通过查询原理图可以得:

    绿色LED为GPIO2_3引脚,换算成管脚号为2×8+3=19。(Hi3516一组GPIO含有8个引脚),输出高电平点亮。

    红外LED为GPIO为GPIO5_1引脚,换算成管脚号为5×8+1=41,输出高电平点亮。

    HDF驱动开发流程解析

    这样就可以修改我们的驱动代码,注意需要包含#include "gpio_if.h"头文件用于控制GPIO,修改代码如下。

    HDF驱动开发流程解析

    // Initialize the driver service.
    static int32_t HdfHelloLiteosDriverInit(struct HdfDeviceObject *deviceObject) {
    HDF_LOGI("Hello LiteOS");
    GpioSetDir(19,GPIO_DIR_OUT);//引脚设置为输出
    GpioWrite(19,GPIO_VAL_HIGH);//输出高电平,绿色LED亮
    return 0;
    }
    // Initialize the driver service.
    static int32_t HdfHelloLinuxDriverInit(struct HdfDeviceObject *deviceObject) {
    HDF_LOGI("Hello Linux");
    GpioSetDir(41,GPIO_DIR_OUT);//引脚设置为输出
    GpioWrite(41,GPIO_VAL_HIGH);//输出高电平,红外灯亮
    return 0;
    }

    在创建完成驱动代码后,还要把它加入OpenHarmony的构建系统,LiteOS内核与Linux内核的编译配置流程会有所不同。

    涉及Makefile和BUILD.gn修改:

    可以看到在drivers/adapter/khdf/liteos/model新增hello_liteos目录,用于存放驱动编译规则代码,目录下新建了BUILD.gn以及Makefile文件,Kconfig文件文件不参与到驱动编译目前暂不考虑。

    HDF驱动开发流程解析

    1. 驱动代码的编译必须要使用HDF框架提供的Makefile模板进行编译,在新建的MakeFile文件新建如下代码,生成编译结构文件,也就是生成对应MODULE_NAME的静态库。
    include $(LITEOSTOPDIR)/../../drivers/adapter/khdf/liteos/lite.mk#导入hdf预定义内容
    HELLO_LITEOS_ROOT_DIR = $(LITEOSTOPDIR)/../../drivers/framework/model/hello_liteos
    ifeq ($(LOSCFG_DRIVERS_HDF_HELLO_LITEOS), y)
    MODULE_NAME := hdf_hello_liteos_driver #生成的结果文件
    LOCAL_INCLUDE := $(HELLO_LITEOS_ROOT_DIR)/../../../../../third_party/FreeBSD/sys/dev/evdev#本驱动的头文件目录
    LOCAL_SRCS += $(HELLO_LITEOS_ROOT_DIR)/driver/hello_liteos_driver.c#本驱动的源代码文件
    endif
    include $(HDF_DRIVER)#导入Makefile模板完成编译
    1. 在drivers/adapter/khdf/liteos/hdf_lite.mk将编译结果文件链接到内核镜像。
    ifeq ($(LOSCFG_DRIVERS_HDF_HELLO_LITEOS), y)
    LITEOS_BASELIB += -lhdf_hello_liteos_driver #链接生成的静态库
    LIB_SUBDIRS += $(LITEOS_DRIVERS_HDF)/model/hello_liteos #驱动代码Makefile的目录
    endif

    编译流程关系如下图:

    HDF驱动开发流程解析

    1. 新增模块BUILD.gn代码如下:
    import("//drivers/adapter/khdf/liteos/hdf.gni")
    module_switch = defined(LOSCFG_DRIVERS_HDF_HELLO_LITEOS)
    module_name = "hdf_hello_liteos_driver"#模块名
    hdf_driver(module_name) {
    FRAMEWORKS_HELLO_LITEOS_ROOT = "$HDF_FRAMEWORKS_PATH/model/hello_liteos"
    #模块要编译的源码文件
    sources = ["$FRAMEWORKS_HELLO_LITEOS_ROOT/driver/hello_liteos_driver.c"]
    #依赖的头文件目录
    include_dirs = [
    "//third_party/FreeBSD/sys/dev/evdev/"
    ]
    }
    1. 把新增模块的BUILD.gn所在的目录添加到/drivers/adapter/khdf/liteos/BUILD.gn里面:
      由于生成的代码是在model模块下的,model已经添加编译到了/drivers/adapter/khdf/liteos/BUILD.gn下,故我们只需在model层下的BUILD.gn下添加模块,也就是在drivers/adapter/khdf/liteos/model/BUILD.gn添加hello_liteos模块到modules中。
    import("//drivers/adapter/khdf/liteos/hdf.gni")

    module_group("model") {
    modules = [
    "audio",
    "bus/usb",
    "display",
    "input",
    "hello_liteos", //新增模块
    "misc/dsoftbus",
    "misc/light",
    "misc/vibrator",
    "network/ethernet",
    "network/wifi",
    "sensor",
    "storage",
    "usb/device",
    "usb/host",
    ]
    }

    用下面这张图来表示他们的编译依赖关系可能会比较清晰一点。

    HDF驱动开发流程解析

    在drivers/adapter/khdf/linux/model目录下可以发现新增了新建hello_linux文件夹,用于存放驱动编译规则代码,目录下新建了MakeFile文件用于编译驱动配置。

    HDF驱动开发流程解析

    1. 新增模块Makefile文件里面模块代码编译规则代码如下:
    HELLO_LINUX_ROOT_DIR = ../../../../../framework/model/hello_linux/driver
    obj-$(CONFIG_DRIVERS_HDF_HELLO_LINUX) += \
    $(HELLO_LINUX_ROOT_DIR)/hello_linux_driver.o##将hello_linux_driver.o文件编译
    ccflags-y += -I$(srctree)/drivers/hdf/framework/include/core \
    -I$(srctree)/drivers/hdf/framework/core/common/include/host \
    -I$(srctree)/drivers/hdf/framework/include/utils \
    -I$(srctree)/drivers/hdf/framework/include/osal \
    -I$(srctree)/drivers/hdf/framework/ability/sbuf/include \
    -I$(srctree)/drivers/hdf/framework/include/platform \
    -I$(srctree)/drivers/hdf/framework/include/config \
    -I$(srctree)/drivers/hdf/framework/core/host/include \
    -I$(srctree)/drivers/hdf/framework/core/shared/include \
    -I$(srctree)/drivers/hdf/framework/utils/include \
    -I$(srctree)/drivers/hdf/khdf/osal/include
    ccflags-y +=-I$(srctree)/bounds_checking_function/include \
    -I$(srctree)/drivers/hdf/evdev
    1. 添加模块目录到drivers/adapter/khdf/linux/Makefile:
    obj-$(CONFIG_DRIVERS_HDF) += manager/
    obj-$(CONFIG_DRIVERS_HDF_PLATFORM) += platform/
    obj-$(CONFIG_DRIVERS_HDF_DISP) += model/display/
    obj-$(CONFIG_DRIVERS_HDF_INPUT) += model/input/
    obj-$(CONFIG_DRIVERS_HDF_WIFI) += model/network/wifi/
    obj-$(CONFIG_DRIVERS_HDF_USB_PNP_NOTIFY) += model/usb/host/
    obj-$(CONFIG_DRIVERS_HDF_USB_F_GENERIC) += model/usb/device/
    obj-$(CONFIG_DRIVERS_HDF_SENSOR) += model/sensor/
    obj-$(CONFIG_DRIVERS_HDF_STORAGE) += model/storage/
    obj-$(CONFIG_DRIVERS_HDF_BT) += model/network/bluetooth/
    obj-$(CONFIG_DRIVERS_HDF_LIGHT) += model/misc/light/
    obj-$(CONFIG_DRIVERS_HDF_VIBRATOR) += model/misc/vibrator/
    obj-$(CONFIG_DRIVERS_HDF_AUDIO) += model/audio/
    obj-$(CONFIG_DRIVERS_HDF_DSOFTBUS) += model/misc/dsoftbus/
    obj-$(CONFIG_DRIVERS_HDF_HELLO_LINUX) += model/hello_linux/#新增的模块目录

    编译流程关系如下:

    HDF驱动开发流程解析

    如果需要定义模块控制宏,也可以对Kconfig进行配置,在通过工具生成的文件中可以发现还包含Kconfig可视化配置文件,该功能基于Kconfiglib与Kconfig实现,方便用户个性化配置OpenHarmony产品子系统部件。

    基于Kconfig实现的可视化配置功能具有以下优点:

    • 能直观且全面地展示软件的部件选项。
    • 可靠性强,如Linux-kernel、buildroot等知名软件都采用Kconfig进行可视化配置。

    HDF驱动开发流程解析

    目前这个功能对我来说还没什么用,还未深入研究,对于HDF驱动编译配置中没什么影响,可以不进行配置。感兴趣的话可以看看下面这个指导进行学习。

    ​编译构建Kconfig可视化配置指导​​。

    HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述,因此在vendor/hisilicon/hispark_taurus(_linux)/hdf_config/device_info/device_info.hcs下添加分别驱动设备描述,模板如下:

    sample_host :: host{
    hostName = "host0"; // host名称,host节点是用来存放某一类驱动的容器。
    priority = 100; // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序。
    caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; // 用户态进程Linux capabilities配置。
    device_sample :: device { // sample设备节点
    device0 :: deviceNode { // sample驱动的DeviceNode节点
    policy = 1; // policy字段是驱动服务发布的策略,在驱动服务管理章节有详细介绍。
    priority = 100; // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序。
    preload = 0; // 驱动按需加载字段,在本章节最后的说明有详细介绍。
    permission = 0664; // 驱动创建设备节点权限
    moduleName = "sample_driver"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。
    serviceName = "sample_service"; // 驱动对外发布服务的名称,必须唯一。
    deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等。
    }
    }
    }

    DevEco Device Tool为我们生成的配置如下,可根据需要进行修改:

    HDF驱动开发流程解析

    由上图可以看到其中deviceMatchAttr为空,并没有为我们生成私有配置信息,如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息。HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject中的property里面,通过Bind和Init传递给驱动。驱动的配置信息示例如下:

    root {
    SampleDriverConfig {
    sample_version = 1;
    sample_bus = "I2C_0";
    match_attr = "sample_config"; // 该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
    }
    }

    配置信息定义之后,需要将该配置文件添加到板级配置入口文件hdf.hcs(目录位于vendor/hisilicon/hispark_taurus(_linux)/hdf_config/hdf.hcs),这里就不进行配置了,等下次尝试加入用户态业务代码再演示,示例如下。

    #include "device_info/device_info.hcs"
    #include "sample/sample_config.hcs"

    对两个内核的工程分别进行编译烧录运行。

    HDF驱动开发流程解析

    结果如下:

    Linux内核:

    HDF驱动开发流程解析

    HDF驱动开发流程解析

    LiteOS内核:

    HDF驱动开发流程解析

    ​想了解更多关于开源的内容,请访问:​

    ​51CTO 开源基础软件社区​

    ​https://ost.51cto.com​​。

    © 版权声明

    相关文章