序
最近忙于适配OpenHarmonyOS LiteOS-M 平台,已经成功实践适配平台GD32F407、STM32F407、STM32G474板卡,LiteOS适配已经算是有实际经验了。
但是,鸿蒙代码学习进度慢下来了。还是得不断学习理论知识丰富自己的认知。接下来时间要把HDF驱动框架熟悉,完善南向开发技术点。
一、概述
HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理、驱动消息机制和配置管理。并以组件化驱动模型作为核心设计思路,让驱动开发和部署更加规范,旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的驱动管理的开发环境,力求做到一次开发,多系统部署。
HDF支持两种加载方式:
- 按需加载, HDF框架支持驱动在系统后动过程中默认加载,或者在系统后动之后动态加载。
- 按序加载, HDF框架支持驱动在系统启动的过程中按照驱动的优先级进行加载。
驱动开发步骤:
- 驱动程序实现,包含驱动业务代码和驱动入口注册到HDF框架。
- 驱动编译,将业务代码进行编译,同时将结果编译进内核。
- 驱动配置,HDF使用HCS作为配置描述源码驱动配置包含两部分HDF框架定义的驱动设备描述和驱动的私有配置信息。
二、HDF驱动框架
HDF驱动框架主要包含三部分:
- 1、驱动程序部分----完成驱动的功能逻辑。
- 2、驱动配置信息----指示驱动的加载信息内容。
- 3、驱动资源配置----配置驱动的硬件配置信息。
三、驱动程序
驱动程序主要是完成驱动功能的逻辑代码,轻量内核LiteOS-M驱动程序代码路径为:drivers/hdf_core/adapter/platform 。
SDK源码使用drivers/hdf_core/adapter/platform目录,放置适配LiteOS-M各个平台驱动程序。
drivers/hdf_core/adapter/platform
├── BUILD.gn
├── can
├── gpio
├── i2c
├── mipi_dsi
├── pwm
├── spi
├── uart
└── watchdog
这里以LiteOS-M GPIO HDF驱动为例,对于开发者首先看到的是驱动入口部分,驱动入口通过结构体DriverEntry进行描述。其中主要包含Bind, Init 和Release三个接口。
/* HdfDriverEntry definitions */
//struct HdfDriverEntry定义在:drivers/hdf_core/interfaces/inner_api/host/shared/hdf_device_desc.h
struct HdfDriverEntry g_GpioDriverEntry = {
.moduleVersion = 1,
.moduleName = "ST_GPIO_MODULE_HDF", // 职责:与hdf hcs结点moduleName进行匹配
.Bind = GpioDriverBind, // 职责:绑定驱动对外提供的服务接口到HDF
.Init = GpioDriverInit, // 职责:初始化驱动自身的业务
.Release = GpioDriverRelease, // 职责:释放驱动资源,发生异常时也会调用
};
HDF_INIT(g_GpioDriverEntry); //注册到HDF框架中
Bind接口描述:该接口的作用主要是完成驱动设备和设备服务接口的bind动作。
static int32_t GpioDriverBind(struct HdfDeviceObject *device)
{
if (device == NULL) {
HDF_LOGE("device object is NULL\n");
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
Init接口描述:当框架完成设备绑定动作后,就开始调用驱动初始化接口,初始化成功后,驱动框架根据配置文件决定是对外创建设备服务接口,还是接口只对当前服务可见。如果Init初始化失败,驱动框架就会主动释放创建的设备接口等信息。
static int32_t GpioDriverInit(struct HdfDeviceObject *device)
{
int32_t ret;
struct GpioCntlr *gpioCntlr = NULL;
if (device == NULL) {
HDF_LOGE("%s: device is NULL", __func__);
return HDF_ERR_INVALID_PARAM;
}
ret = PlatformDeviceBind(&g_stmGpioCntlr.device, device); //绑定设备对象
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: bind hdf device failed:%d", __func__, ret);
return ret;
}
gpioCntlr = GpioCntlrFromHdfDev(device); //获取gpio控制器
if (gpioCntlr == NULL) {
HDF_LOGE("GpioCntlrFromHdfDev fail\r\n");
return HDF_DEV_ERR_NO_DEVICE_SERVICE;
}
ret = AttachGpioDevice(gpioCntlr, device); /* GpioCntlr add GpioDevice to priv */
if (ret != HDF_SUCCESS) {
HDF_LOGE("AttachGpioDevice fail\r\n");
return HDF_DEV_ERR_ATTACHDEV_FAIL;
}
gpioCntlr->ops = &g_GpioCntlrMethod; /* 绑定控制器控制方法 */
ret = GpioCntlrAdd(gpioCntlr);
if (ret != HDF_SUCCESS) {
HDF_LOGE("GpioCntlrAdd fail %d\r\n", gpioCntlr->start);
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
gpio控制器方法实现。
/* GpioMethod definitions */
struct GpioMethod g_GpioCntlrMethod = {
.request = NULL,
.release = NULL,
.write = GpioDevWrite,
.read = GpioDevRead,
.setDir = GpioDevSetDir,
.getDir = GpioDevGetDir,
.toIrq = NULL,
.setIrq = GpioDevSetIrq,
.unsetIrq = GpioDevUnSetIrq,
.enableIrq = GpioDevEnableIrq,
.disableIrq = GpioDevDisableIrq,
};
Release接口描述:当用户需要卸载驱动时,驱动框架先通过该接口通知驱动程序释放资源,然后再释放其他内部资源。
static void GpioDriverRelease(struct HdfDeviceObject *device)
{
struct GpioCntlr *gpioCntlr = NULL;
if (device == NULL) {
HDF_LOGE("%s: device is NULL", __func__);
return;
}
gpioCntlr = GpioCntlrFromHdfDev(device);
if (gpioCntlr == NULL) {
HDF_LOGE("%s: host is NULL", __func__);
return;
}
gpioCntlr->count = 0;
}
四、驱动配置
HCS(HDF Configuration Source)是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。
驱动配置包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息。
设备描述信息
HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述,因此基于HDF框架开发的驱动必须要在HDF框架定义的device_info.hcs配置文件中添加对应的设备描述。
root {
module = "navy,stm32g474";
device_info {
match_attr = "hdf_manager";
template host {
hostName = "";
priority = 100;
template device {
template deviceNode {
policy = 0;
priority = 100;
preload = 0;
permission = 0664;
moduleName = "";
serviceName = "";
deviceMatchAttr = "";
}
}
}
platform :: host {
hostName = "platform_host"; // 容器名称
priority = 50; // 容器启动优先级
device_gpio :: device {
gpio0 :: deviceNode {
policy = 0; // 详见ServicePolicy
priority = 60; // 驱动启动优先级
permission = 0644; // 驱动创建设备节点权限
moduleName = "ST_GPIO_MODULE_HDF"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致
serviceName = "HDF_PLATFORM_GPIO"; // 服务名称
deviceMatchAttr = "gpio_config"; // 【必要】用于配置控制器私有数据,要与gpio_config.hcs中对应控制器保持一致,其他控制器信息也在文件中
}
}
device_xxx :: device {
xxx :: deviceNode {
...
}
}
}
misc :: host {
hostName = "misc_host";
priority = 100;
xxx :: device {
xxx :: deviceNode {
...
}
}
}
}
}
其中,
HDF框架定了驱动对外发布服务的策略,是由配置文件中的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;
驱动私有配置信息
#include "default_device_info.hcs"
root {
platform {
gpio_config {
match_attr = "gpio_config"; // 【必要】必须和device_info.hcs中的deviceMatchAttr值一致
pin = [ 0, 1, 2, 3, 4, 5, 6]; // pin index when register to hdf framework 0-3 spi flash gpio 4-5 uart1
realPin = [ 5, 5, 4, 15, 6, 7, 13]; // pin number in stm32 led2 pe5, led3 pe6,
group = [ 0, 1, 1, 0, 1, 1, 2]; // group of gpio 0:GPIOA 1:GPIOB 2:GPIOC 3:GPIOD 4:GPIOE 5: GPIOF 6:GPIOG 7:GPIOH 8:GPIOI
mode = [ 2, 2, 2 ,1, 2, 2, 1]; // 0: input 1: output 2:alternate 3:analog
speed = [ 3, 3, 3, 3, 3, 3, 0]; // 0: low 1: middle 2:high 3:very_high
pull = [ 0, 0, 0, 0, 1, 1, 1]; // 0: nopull 1:up 2:down
pinNum = 7;
output = [ 0, 0, 0, 0, 0, 0, 0]; // 0:pushpull 1:opendrain
alternate = [ 5, 5, 5, 0, 7, 7, 0];
}
...
}
...
}
HDF框架将一类设备驱动放在同一个Host(设备容器)里面,用于管理一组设备的启动加载等过程。在划分Host时,驱动程序是部署在一个Host还是部署在不同的Host,主要考虑驱动程序之间是否存在耦合性,如果两个驱动程序之间存在依赖,可以考虑将这部分驱动程序部署在一个Host里面,否则部署到独立的Host中是更好的选择。Device对应一个真实的物理设备。DeviceNode是设备的一个部件,Device至少有一个DeviceNode。每个DeviceNode可以发布一个设备服务。驱动即驱动程序,每个DevicdNode唯一对应一个驱动,实现和硬件的功能交互。HDF驱动模型如下图所示:
到此基础的HDF驱动开发流程结束。