OpenHarmony之HDF驱动框架
OpenHarmony之HDF驱动框架
目录
概述
HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理、驱动消息机制和配置管理。并以组件化驱动模型作为核心设计思路,让驱动开发和部署更加规范,旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的驱动管理的开发环境,力求做到一次开发,多系统部署。
- 驱动加载
HDF驱动框架提供把和配置的设备列表匹配成功的驱动程序加载起来的功能 - 驱动服务管理
HDF框架可以集中管理驱动服务,开发者可直接通过HDF框架对外提供的能力接口获取驱动相关的服务 - 驱动消息机制
HDF框架提供统一的驱动消息机制,支持用户态应用向内核态驱动发送消息,也支持内核态驱动向用户态应用发送消息 - 配置管理
HCS是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理
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 #提供基础数据结构和算法等
框架交互流程:
参考简单实例: 基于HDF简单驱动开发实例.md
驱动模型
HDF框架将一类设备驱动放在同一个Host(设备容器)里面,用于管理一组设备的启动加载等过程。在划分Host时,驱动程序是部署在一个Host还是部署在不同的Host,主要考虑驱动程序之间是否存在耦合性,如果两个驱动程序之间存在依赖,可以考虑将这部分驱动程序部署在一个Host里面,否则部署到独立的Host中是更好的选择。
Device对应一个真实的物理设备。DeviceNode是设备的一个部件,Device至少有一个DeviceNode。每个DeviceNode可以发布一个设备服务。驱动即驱动程序,每个DevicdNode唯一对应一个驱动,实现和硬件的功能交互。
HDF驱动模型如下图所示:
驱动加载
HDF驱动框架提供把和配置的设备列表匹配成功的驱动程序加载起来的功能,支持按需加载和按序加载两种策略,具体设备的加载策略由配置文件中的preload字段来控制,配置值参考如下:
typedef enum {
DEVICE_PRELOAD_ENABLE = 0,
DEVICE_PRELOAD_ENABLE_STEP2 = 1,
DEVICE_PRELOAD_DISABLE = 2,
DEVICE_PRELOAD_INVALID
} DevicePreload;
按需加载
- preload字段配置为0(DEVICE_PRELOAD_ENABLE),则系统启动过程中默认加载。
- preload字段配置为1(DEVICE_PRELOAD_ENABLE_STEP2),当系统支持快速启动的时候,则在系统完成之后再加载这一类驱动,否则和DEVICE_PRELOAD_ENABLE含义相同。
- preload字段配置为2(DEVICE_PRELOAD_DISABLE),则系统启动过程中默认不加载,支持后续动态加载,当用户态获取驱动服务消息机制时,如果驱动服务不存在,HDF框架会尝试动态加载该驱动。
按序加载(默认加载策略)
配置文件中的priority(取值范围为整数0到200)是用来表示host(驱动容器)和驱动的优先级的。不同的host内的驱动,host的priority值越小,驱动加载优先级越高;同一个host内驱动的priority值越小,加载优先级越高。
异常恢复(用户态驱动)
当驱动服务异常退出时,恢复策略如下:
- preload字段配置为0(DEVICE_PRELOAD_ENABLE)或1(DEVICE_PRELOAD_ENABLE_STEP2)的驱动服务,由启动模块拉起host并重新加载服务。
- preload字段配置为2(DEVICE_PRELOAD_DISABLE)的驱动服务,需业务模块注册HDF的服务状态监听器,当收到服务退出消息时,业务模块调用LoadDevice重新加载服务。
驱动服务管理
驱动服务是HDF驱动设备对外提供能力的对象,由HDF框架统一管理。驱动服务管理主要包含驱动服务的发布和获取。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;
当驱动需要以接口的形式对外提供能力时,可以使用HDF框架的驱动服务管理能力。
驱动消息机制管理
当用户态应用和内核态驱动需要交互时,可以使用HDF框架的消息机制来实现。
消息机制的功能主要有以下两种:
- 用户态应用发送消息到驱动。
- 用户态应用接收驱动主动上报事件。
配置管理
HCS(HDF Configuration Source)是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。HC-GEN(HDF Configuration Generator)是HCS配置转换工具,可以将HDF配置文件转换为软件可读取的文件格式:
- 在弱性能环境中,转换为配置树源码或配置树宏定义,驱动可直接调用C代码或宏式APIs获取配置。
- 在高性能环境中,转换为HCB(HDF Configuration Binary)二进制文件,驱动可使用HDF框架提供的配置解析接口获取配置。
以下是使用HCB模式的典型应用场景:
图2 配置使用流程图
HCS经过HC-GEN编译生成HCB文件,HDF驱动框架中的HCS Parser模块会从HCB文件中重建配置树,HDF驱动模块使用HCS Parser提供的配置读取接口获取配置内容。
配置语法
具体细节在此省略,后面会有些例子。
配置生成
hc-gen
是配置生成的工具,可以对HCS配置语法进行检查并把HCS源文件转化成HCB二进制文件,类似于设备树工具dtc
主要常用用法:
- 生成.c/.h配置文件方法:
hc-gen -o [OutputCFileName] -t [SourceHcsFileName]
- 生成HCB配置文件方法:
hc-gen -o [OutputHcbFileName] -b [SourceHcsFileName]
- 生成宏定义配置文件方法:
hc-gen -o [OutputMacroFileName] -m [SourceHcsFileName]
- 反编译HCB文件为HCS方法:
hc-gen -o [OutputHcsFileName] -d [SourceHcbFileName]
平台驱动举例
适配最主要的工作就是根据具体硬件实现适配层相关的钩子函数,并配置好相应的属性文件,即hcs,大致分为以下步骤:
- 实例化驱动入口
- 配置属性文件
- 实例化UART控制器对象
- 驱动调试
统一服务模式
在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层
比如I2C模块:
device_info.hcs
配置参考root { device_info { match_attr = "hdf_manager"; device_i2c :: device { device0 :: deviceNode { policy = 2; priority = 50; permission = 0644; moduleName = "HDF_PLATFORM_I2C_MANAGER"; serviceName = "HDF_PLATFORM_I2C_MANAGER"; deviceMatchAttr = "hdf_platform_i2c_manager"; } device1 :: deviceNode { policy = 0; // 等于0,不需要发布服务。 priority = 55; // 驱动启动优先级。 permission = 0644; // 驱动创建设备节点权限。 moduleName = "hi35xx_i2c_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 serviceName = "HI35XX_I2C_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一。 deviceMatchAttr = "hisilicon_hi35xx_i2c"; //【必要】用于配置控制器私有数据,要与i2c_config.hcs中对应控制器保持一致, // 具体的控制器信息在 i2c_config.hcs中。 } } } }
i2c_config.hcs
配置参考root { platform { i2c_config { match_attr = "hisilicon_hi35xx_i2c"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致 template i2c_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。 bus = 0; // 【必要】i2c识别号 reg_pbase = 0x120b0000; // 【必要】物理基地址 reg_size = 0xd1; // 【必要】寄存器位宽 irq = 0; // 【可选】中断号,由控制器的中断特性决定是否需要 freq = 400000; // 【可选】频率,初始化硬件控制器的可选参数 clk = 50000000; // 【可选】控制器时钟,由控制器时钟的初始化流程决定是否需要 } controller_0x120b0000 :: i2c_controller { bus = 0; } controller_0x120b1000 :: i2c_controller { bus = 1; reg_pbase = 0x120b1000; } ... } } }
独立服务模式
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
比如UART模块:
device_info.hcs
配置参考:
root {
device_info {
match_attr = "hdf_manager";
platform :: host {
hostName = "platform_host";
priority = 50;
device_uart :: device {
device0 :: deviceNode {
policy = 1; // 驱动服务发布的策略,policy大于等于1(用户态可见为2,仅内核态可见为1)。
priority = 40; // 驱动启动优先级
permission = 0644; // 驱动创建设备节点权限
moduleName = "HDF_PLATFORM_UART"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。
serviceName = "HDF_PLATFORM_UART_0"; // 驱动对外发布服务的名称,必须唯一,必须要按照HDF_PLATFORM_UART_X的格式,X为UART控制器编号。
deviceMatchAttr = "hisilicon_hi35xx_uart_0"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值一致。
}
device1 :: deviceNode {
policy = 2;
permission = 0644;
priority = 40;
moduleName = "HDF_PLATFORM_UART";
serviceName = "HDF_PLATFORM_UART_1";
deviceMatchAttr = "hisilicon_hi35xx_uart_1";
}
...... // 如果存在多个UART设备时【必须】添加节点,否则不用
}
}
}
}
uart_config.hcs
配置参考:
root {
platform {
template uart_controller { // 配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值
match_attr = "";
num = 0; // 【必要】端口号
baudrate = 115200; // 【必要】波特率,数值可按需填写
fifoRxEn = 1; // 【必要】使能接收FIFO
fifoTxEn = 1; // 【必要】使能发送FIFO
flags = 4; // 【必要】标志信号
regPbase = 0x120a0000; // 【必要】地址映射需要
interrupt = 38; // 【必要】中断号
iomemCount = 0x48; // 【必要】地址映射需要
}
controller_0x120a0000 :: uart_controller {
match_attr = "hisilicon_hi35xx_uart_0"; // 【必要】必须和device_info.hcs中对应的设备的deviceMatchAttr值一致
}
controller_0x120a1000 :: uart_controller {
num = 1;
baudrate = 9600;
regPbase = 0x120a1000;
interrupt = 39;
match_attr = "hisilicon_hi35xx_uart_1";
}
...... // 如果存在多个UART设备时【必须】添加节点,否则不用
}
}
统一服务模式相较独立模式,可以节省资源,方便管理
外设驱动举例
这里列举一个稍微简单点的Light驱动模型,Light驱动模型为上层Light硬件服务层提供稳定的灯控制能力接口,包括获取灯类型、配置点灯模式、配置灯闪烁效果、点灯、熄灯等。
Light驱动模型流程:
以标准系统RK3568为例,介绍Light模块驱动加载及运行流程:
- Device Manager从
device_info.hcs
配置文件中读取Light设备管理配置信息。 - Device Manager从
light_config.hcs
配置文件中读取Light数据配置信息。 - HCS Parser解析Light设备管理配置信息,加载对应的Light Host,并控制Host完成驱动的加载。
- Light Proxy获取到Light HDI接口服务实例后,通过IPC(Inter-Process Communication)调用到Light Stub。
- Light Stub主要处理与IPC相关的业务逻辑,完成参数反序列化后调用Light Controller。
- Light Controller中是HDI接口的真正实现,通过IPC调用Light抽象驱动接口,进一步操作Light硬件设备。
具体代码可参看:Light驱动模型.md
思考总结
- HDF最核心几大块:配置管理,驱动管理,对外服务,消息机制
- 对外接口侧重于服务,消息,而不是设备节点,这个是很大的一个转变