- OpenHarmony HDF 框架
1.OpenHarmony HDF 框架概述
OpenHarmony驱动子系统采用C面向对象编程模型构建,通过平台解耦、内核解耦,兼容不同内核,提供了归一化的驱动平台底座,旨在为开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。
HDF驱动框架架构图
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驱动框架开发的驱动主要包含三大部分:
- 驱动程序部分 - 完成驱动的功能逻辑。
- 驱动配置信息 - 指示驱动的加载信息内容。
- 驱动资源配置 - 配置驱动的硬件配置信息。
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