Bootstrap

OpenHarmony-3.驱动HDF

  • OpenHarmony HDF 框架

1.OpenHarmony HDF 框架概述

  OpenHarmony驱动子系统采用C面向对象编程模型构建,通过平台解耦、内核解耦,兼容不同内核,提供了归一化的驱动平台底座,旨在为开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。

  HDF驱动框架架构图
zh-cn_image_0000001351387426

  HDF驱动架构主要组成部分:

  • HDI(Hardware Device Interface,硬件设备统一接口)层:通过规范化的设备接口标准,为系统提供统一、稳定的硬件设备操作接口。

  • HDF驱动框架:提供统一的硬件资源管理、驱动加载管理、设备节点管理、设备电源管理以及驱动服务模型等功能,需要包含设备管理、服务管理、DeviceHost、PnPManager等模块。

  • 统一的配置界面:支持硬件资源的抽象描述,屏蔽硬件差异,可以支撑开发者开发出与配置信息不绑定的通用驱动代码,提升开发及迁移效率,并可通过HC-Gen等工具快捷生成配置文件。

  • 操作系统抽象层(OSAL,Operating System Abstraction Layer):提供统一封装的内核操作相关接口,屏蔽不同系统操作差异,包含内存、锁、线程、信号量等接口。

  • 平台驱动:为外设驱动提供Board硬件(如:I2C/SPI/UART总线等平台资源)操作统一接口,同时对Board硬件操作进行统一的适配接口抽象以便于不同平台迁移。

  • 外设驱动模型:面向外设驱动,提供常见的驱动抽象模型,主要达成两个目的,提供标准化的器件驱动,开发者无需独立开发,通过配置即可完成驱动的部署;提供驱动模型抽象,屏蔽驱动与不同系统组件间的交互,使得驱动更具备通用性。

2.HDF 架构

  OpenHarmony驱动框架采用主从架构设计模式,围绕着框架、模型、能力库和工具四个维度能力展开构建。
在这里插入图片描述

  • 驱动框架 -位于framework/core目录
    提供驱动框架能力,主要完成驱动加载和启动功能。
    通过对象管理器方式可实现驱动框架的弹性化部署和扩展。

  • 驱动模型 - 位于framework/model目录
    提供了模型化驱动能力,如网络设备模型。

  • 驱动能力库 - 位于framework/ability目录
    提供基础驱动能力模型,如IO通信能力模型。

  • 驱动工具 - 位于framework/tools目录
    提供HDI接口转换、驱动配置编译等工具。

  • 驱动接口 - 位于lite/hdi目录
    提供规范化的驱动接口。

  • Support - 位于framework/support目录
    提供规范化的平台驱动接口和系统接口抽象能力。

2.1.HDF 驱动框架

在这里插入图片描述
  OpenHarmony 系统 HDF 驱动框架主要由:

  • 驱动基础框架
    HDF 驱动基础框架提供统一的硬件资源管理,驱动加载管理以及设备节点管理等功能。驱动框架采用的是主从模式设置,由 device manager 和 device host 组成。device manager 提供了统一的驱动管理,device manager 启动时根据 device information 提供驱动设备信息加载相应的驱动 device host,并控制 host 完成驱动的加载。 device host 提供驱动运行的环境,同时预置 host framework 与 device manager 进行协同,完成驱动加载和调用。根据业务的需求 device host 可以有多个实例。
  • 驱动程序
    驱动程序实现驱动具体的功能,每个驱动由一个或者多个驱动程序组成,每个驱动程序都对应着一个 driver entry。driver entry 主要完成驱动的初始化和驱动接口绑定功能。
  • 驱动配置文件
    驱动配置文件(.hcs),主要由设备信息(device information)和设备资源(device resource)组成,device information 完成设备信息的配置(如配置接口发布策略,驱动加载方式等)。device resource 完成设备资源的配置(如 GPIO 管脚、寄存器等资源信息配置等)。
  • 驱动接口
    驱动接口(HDI),提供标准化的接口定义和实现,驱动框架提供 IO services 和 IO dispatcher 机制,是的不同部署形态下驱动接口趋于形式一致。

2.2.驱动框架交互流程
在这里插入图片描述
  驱动框架完成大部分驱动加载的动作,用户只需注册自己所需的接口和配置,然后驱动框架就会解析配置的内容,完成驱动加载和初始化动作。

  开发者基于HDF驱动框架开发的驱动主要包含三大部分:

  1. 驱动程序部分 - 完成驱动的功能逻辑。
  2. 驱动配置信息 - 指示驱动的加载信息内容。
  3. 驱动资源配置 - 配置驱动的硬件配置信息。

2.3.驱动开发

2.3.1.平台驱动

  OpenHarmony平台驱动(Platform Driver),即平台设备(Platform Device)驱动,为系统及外设驱动提供访问接口。这里的平台设备,泛指I2C/UART等总线、以及GPIO/RTC等特定硬件资源。平台驱动框架是OpenHarmony驱动框架的重要组成部分,它基于HDF驱动框架、操作系统适配层以及驱动配置管理机制,为各类平台设备驱动的实现提供标准模型。平台驱动框架为外设提供了标准的平台设备访问接口,使其不必关注具体硬件;同时为平台设备驱动提供统一的适配接口,使其只关注自身硬件的控制。

  平台驱动框架提供如下特性:

  • 统一的平台设备访问接口:对平台设备操作接口进行统一封装,屏蔽不同SoC平台硬件差异以及不同OS形态差异。

  • 统一的平台驱动适配接口:为平台设备驱动提供统一的适配接口,使其只关注自身硬件的控制,而不必关注设备管理及公共业务流程。

  • 提供设备注册、管理、访问控制等与SoC无关的公共能力。

  平台驱动框架目前支持的设备类型包括但不限于:ADC、DAC、GPIO、HDMI、I2C、I3C、MIPI_CSI、MIPI_DSI、MMC、Pin、PWM、Regulator、RTC、SDIO、SPI、UART、WatchDog等。

2.3.2.外设驱动
  OpenHarmony在HDF驱动框架及平台驱动框架的基础上,面向外设器件驱动,提供常见的驱动抽象模型。它提供标准化的外设器件驱动,可以减少重复开发;同时提供统一的外设驱动模型抽象,屏蔽驱动与不同系统组件间的交互,使得驱动更具备通用性。

  OpenHarmony当前支持的外设设备类型包括但不限于:Audio、Camera、Codec、Face_auth、Fingerprint_auth、LCD、Light、Motion、Pin_auth、Sensor、Touchscreen、USB、User_auth、Vibrator、WLAN等。

2.3.3.驱动代码

  HDF驱动架构实现根据功能和模块分为多个代码仓,如下表所示。

在这里插入图片描述

代码下载:

https://gitee.com/openharmony/drivers_hdf_core.git

  • framework 目录代码结构如下:
/drivers/hdf_core/framework
├── core           #实现驱动框架的核心代码
│   ├── adapter    #实现对内核操作接口适配,提供抽象化的接口供开发者使用
│   ├── common     #驱动框架公共基础代码
│   ├── host       #驱动宿主环境模块
│   ├── manager    #驱动框架管理模块
│   └── shared     #host和manager共享模块代码
├── include        #驱动框架对外提供能力的头文件
│   ├── audio      #AUDIO对外提供能力的头文件
│   ├── bluetooth  #蓝牙对外提供能力的头文件
│   ├── core       #驱动框架对外提供的头文件
│   ├── ethernnet  #以太网操作相关的头文件
│   ├── net        #网络数据操作相关的头文件
│   ├── osal       #系统适配相关接口的头文件
│   ├── platform   #平台设备相关接口的头文件
│   ├── utils      #驱动框架公共能力的头文件
│   └── wifi       #WLAN对外提供能力的头文件
├── model          #提供驱动通用框架模型
│   ├── audio      #AUDIO框架模型
│   ├── display    #显示框架模型
│   ├── input      #输入框架模型
│   ├── misc       #杂项设备框架模型,包括dsoftbus、light、vibrator
│   ├── network    #WLAN框架模型
│   └── sensor     #Sensor驱动模型
│   └── storage    #存储驱动模型
│   └── usb        #USB驱动模型
├── sample         #HCS配置描述示例及HDF驱动示例
├── support        #提系统的基础能力 
│   └── platform   #平台设备驱动框架及访问接口,范围包括GPIO、I2C、SPI等
│   └── posix      #posix框架及访问接口,范围包括Mem、Mutex、Sem、Spinlock、Thread、Time等
├── test           #测试用例
├── tools          #hdf框架工具相关的源码
│   └── hc-gen     #配置管理工具源码
│   └── hcs-view   #
│   └── hdf-dbg    #
│   └── hdf-dev_eco_tool #
│   └── hdf-gen    #
│   └── idl-gen    #
│   └── leagecy    #
└── utils          #提供基础数据结构和算法等
  • adapter 目录代码结构如下:
 14 /drivers/hdf_core/adapter
 15 ├── khdf/liteos         # HDF dependent adapter for LiteOS-A kernel
 16 ├── khdf/liteos_m       # HDF dependent adapter for LiteOS-M kernel
 17 ├── uhdf                # HDF user mode interface dependent adapter
 18 └── uhdf2               # HDF user mode dependent adapter

2.3.4. Input 驱动框架
在这里插入图片描述

2.4.HDF驱动加载过程

  OpenHarmony 驱动主要部署在内核态,根据驱动程序部署的不同方式,存在两种驱动加载方式:

  • 动态加载方式:采用传统的so(共享库)加载方式,驱动程序通过指定Symbol方式找到驱动函数入口进行加载。
  • 静态加载方式:采用将驱动程序通过Scatter编译到指定的Section,再通过访问指定Section对应的地址,找到驱动函数入口进行加载。

  当前主要采用静态链接方式,随内核子系统编译和系统镜像打包。
在这里插入图片描述
  HDF驱动加载包括按需加载和按序加载。

  • 按需加载是HDF框架支持驱动在系统启动过程中默认加载,或者在系统启动之后动态加载。
    HDF框架定义的驱动按需加载方式的策略是由配置文件中的 preload 字段来控制,preload 字段的取值范围以及含义如下:
    在这里插入图片描述
  • 按序加载是HDF框架支持驱动在系统启动的过程中按照驱动的优先级进行加载。
    驱动的按序加载是通过配置文件中的 priority(取值范围为整数 0 到 200)来决定的,priority 值越小,表示的优先级越高。驱动的加载顺序,优先根据 host 的 priority 决定,如果host 的 priority 相同,再根据 host 内的驱动 priority 值来决定加载顺序。

2.4.1.HdfDriverEntry 结构体

  HdfDriverEntry 称为HDF驱动入口结构体,代表设备驱动的入口。

drivers/hdf_core/interfaces/inner_api/host/shared/hdf_device_desc.h:
struct HdfDriverEntry {
    //驱动程序的版本号
    int32_t moduleVersion;
    //驱动程序的名称,必须与驱动配置文件device_info.hcs中设备节点的moduleName属性保持一致。
    const char *moduleName;
    //三个函数指针,指向设备驱动的三个入口函数。
    int32_t (*Bind)(struct HdfDeviceObject *deviceObject);
    int32_t (*Init)(struct HdfDeviceObject *deviceObject);
    void (*Release)(struct HdfDeviceObject *deviceObject);
};

  在结构体HdfDriverEntry中,三个函数指针所指向的函数被称为 驱动入口函数 ,由驱动开发者负责编写,被HDF驱动框架调用。

  • Bind函数
    用于向HDF驱动框架告知(注册)驱动程序的驱动服务入口。

  • Init函数
    主要是用来完成驱动的一些初始化动作,比如:通过HDF提供的接口获取设备的配置信息、初始化设备、订阅驱动提供的服务等等。

  • Release函数
    用来释放驱动程序所占用资源。

  HDF驱动框架在加载驱动的过程中,首先调用Bind函数,然后再调用Init函数。当加载驱动的过程中出现异常,或者当驱动被卸载的时候,HDF驱动框架会调用Release函数释放驱动所占用的资源。

  案例:
在这里插入图片描述
  通过HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。

  • HDF_INIT宏展开
#define HDF_INIT(module)  HDF_DRIVER_INIT(module)

#define USED_ATTR __attribute__((used))
#define HDF_SECTION __attribute__((section(".hdf.driver")))
#define HDF_DRIVER_INIT(module) \
 const size_t USED_ATTR module##HdfEntry HDF_SECTION = (size_t)(&(module))

在这里插入图片描述
  HDF_INIT 宏是定义了一个“驱动模块名+HdfEntry”的符号放到".hdf.driver"所在 section,该符号指向的内存地址即为驱动程序入口结构体的地址。这个特殊的 section 将用于开机启动时查找设备驱动。

2.4.2.驱动配置

  驱动配置包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息。HDF使用HCS作为配置描述源码,内容以 Key-Value 键值对为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。HC-GEN (全称 HDF Configuration Generator) 是 HCS 配置转换工具,可以将 HDF 配置文件转换为软件可读取的文件格式:在弱性能环境中,转换为配置树源码,驱动可直接调用 C代码获取配置;在高性能环境中,转换为 HCB(HDF Configuration Binary)二进制文件,驱动可使用 HDF框架提供的配置解析接口获取配置。
HCS经过HC-GEN编译生成HCB文件,HDF驱动框架中的HCS Parser模块会从HCB文件中重建配置树,HDF驱动模块使用HCS Parser提供的配置读取接口获取配置内容。驱动配置过程的原理图如下所示:
在这里插入图片描述

  • 驱动设备描述(必选)
    HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述,因此基于HDF框架开发的驱动必须要在HDF框架定义的device_info.hcs配置文件中添加对应的设备描述,驱动的设备描述填写如下所示:
sample_host :: host{
    hostName = "host0"; //host名称,host节点是用来存放某一类驱动的容器。
    priority = 100; //host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序。
    device_sample :: device { //sample设备节点。
        device0 :: deviceNode { //sample驱动的DeviceNode节点。
        policy = 1; //驱动服务发布的策略
        priority = 100; //驱动启动优先级(0-200),值越大优先级越低,建议默认配 100,优先级相同则不保证 device 的加载顺序
        preload = 0; //驱动按需加载字段
        permission = 0664;//驱动创建设备节点权限
        moduleName = "sample_driver"; //驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
        serviceName = "sample_service"; //驱动对外发布服务的名称,必须唯一
        deviceMatchAttr = "sample_config";//驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等。
        }
    }
}
  • 驱动私有配置信息(可选)
    如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息,HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject 中的property里面,通过Bind和Init传递给驱动,驱动的配置信息示例如下:
root {  
    SampleDriverConfig {
        sample_version = 1; 
        sample_bus = "I2C_0"; 
        match_attr = "sample_config";   //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
    }
}

2.4.3.获取驱动列表

  HDF驱动框架通过将驱动程序入口符号的地址集中存放到一个特殊的 section 来实现对驱动的索引,这个section的开头和末尾插入了_hdf_drivers_start、_hdf_drivers_end两个特殊符号,用于标记这个 section 的范围,两个特殊符号之间的数据即为驱动实现指针。

在这里插入图片描述 2.4.4.获取设备列表

在这里插入图片描述
  配置文本编译后会变成二进制格式的配置文件,其中设备相关信息被存放在一个用“hdf_manager”标记的 device_info 配置块中,host的内容以块的形式在device_info 块中依次排列,host块中记录了host名称、启动优先级和设备列表信息**。设备信息中的 moduleName字段将用于和驱动程序入口中的moduleName进行匹配,从而为设备匹配到正确的驱动程序,完成设备与驱动的匹配**。

2.4.5.设备与驱动匹配
在这里插入图片描述

加载流程图如下:
在这里插入图片描述
2.4.5.驱动框架启动

drivers_hdf_core/adapter/khdf/linux/manager/src/devmgr_load.c:
 25 static int __init DeviceManagerInit(void)
 26 {
 27     int ret;
 28
 29     HDF_LOGD("%s enter", __func__);
 30     ret = DeviceManagerStart();
 31     if (ret < 0) {
 32         HDF_LOGE("%s start failed %d", __func__, ret);
 33     } else {
 34         HDF_LOGD("%s start success", __func__);
 35     }
 36     return ret;
 37 }
 38
 39 late_initcall(DeviceManagerInit);

总结:
1)在系统启动时,DeviceManagerInit通过late_initcall先启动。
2) Device Manager 根据 Device Information 信息,解析配置文件中的 Host 列表,根据 Host 列表中的信息来实例化对应的 Host 对象。
3)Host遍历设备列表去获取与之匹配的驱动程序名称,然后基于驱动程序名称遍历.hdf.driver section 获得驱动程序地址。
4)设备与驱动匹配成功之后,获取指定驱动的入口地址,加载对应的设备驱动程序。
5)调用指定驱动的 Bind 接口,用于关联设备和服务实例。
6)调用指定驱动的 Init 接口,用于完成驱动的相关初始化工作。
7)如果驱动被卸载或者因为硬件等原因 Init 接口返回失败,Release 将被调用,用于释放驱动申请的各类资源。

refer to

  • https://zhuanlan.zhihu.com/p/716011047
  • https://gitee.com/openharmony/drivers_framework/blob/master/README_zh.md
  • https://gitee.com/openharmony/docs/blob/master/zhcn/readme/%E9%A9%B1%E5%8A%A8%E5%AD%90%E7%B3%BB%E7%BB%9F.md
  • https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/driver/driver-overview-foundation.md

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;