2. OpenHarmony系统概述
HDF(Hardware Driver Foundation)是OpenHarmony的驱动基石。很多初学者一上来就啃源码,结果被一堆结构体指针绕晕了。我个人习惯是先搞清楚它的对象模型——说白了,就是HDF怎么抽象硬件、怎么管理驱动的生命周期。
这一章,我们聚焦四个核心对象:驱动对象、设备对象、服务对象、节点对象。你把这四个对象的关系理清了,HDF的骨架也就搭起来了。
2.1 四个核心对象的关系图谱
先上一张图,让你有个整体印象。这张图我画了很多遍,每次给团队新人讲HDF,都是从这张图开始的。
从上图可以看出:驱动对象负责创建设备对象,设备对象负责注册服务对象,而节点对象则是设备在HDF设备树中的“户口本”。
2.2 驱动对象(Driver)
驱动对象是HDF中最上层的抽象。它描述的是“谁在驱动这个硬件”。每个驱动模块在HDF中对应一个HdfDriverEntry结构体。
struct HdfDriverEntry {
int32_t moduleVersion; // 驱动版本号
const char *moduleName; // 驱动名称,全局唯一
int32_t (*Bind)(struct HdfDeviceObject *device); // 绑定设备
int32_t (*Init)(struct HdfDeviceObject *device); // 初始化设备
void (*Release)(struct HdfDeviceObject *device); // 释放设备
};
这里有个关键点:Bind、Init、Release三个回调函数,构成了驱动的生命周期。
生命周期三阶段:
- Bind:驱动与设备绑定,此时可以解析HCS配置、分配资源。
- Init:设备初始化,此时硬件已经就绪,可以注册中断、创建服务。
- Release:驱动卸载,释放所有资源。
我记得有一次排查一个驱动加载失败的问题,发现Bind函数里调用了硬件寄存器操作,结果设备还没上电,直接崩了。嗯,这里要注意:Bind阶段不要操作硬件,那是Init的事。
2.3 设备对象(DeviceObject)
设备对象是驱动与硬件的桥梁。每个驱动实例对应一个HdfDeviceObject。它包含了设备属性、服务列表、私有数据等。
struct HdfDeviceObject {
struct IDeviceIoService *service; // 设备服务接口
struct HdfDeviceObject *parent; // 父设备
struct HdfDeviceObject *children; // 子设备链表
uint32_t deviceId; // 设备ID
const char *deviceName; // 设备名称
struct HdfObject *priv; // 私有数据
};
设备对象最核心的字段是service和priv。service是暴露给上层调用的接口,priv是驱动自己用的上下文数据。
我的习惯:在Bind阶段就把priv分配好,把HCS配置解析后存进去。这样Init阶段直接拿priv里的参数初始化硬件,代码结构清晰很多。
2.4 服务对象(Service)
服务对象是驱动对外提供能力的窗口。上层应用通过HDF框架找到服务,然后调用服务接口操作硬件。
服务对象的核心是IDeviceIoService接口,它定义了一个Dispatch方法:
struct IDeviceIoService {
struct HdfObject object;
int32_t (*Dispatch)(struct IDeviceIoService *service,
int32_t cmdId,
struct HdfSBuf *data,
struct HdfSBuf *reply);
};
上层通过cmdId来区分不同的操作命令。比如cmdId=1表示读寄存器,cmdId=2表示写寄存器。数据通过HdfSBuf传递,这是一个序列化缓冲区,支持跨进程通信。
我曾经踩过的坑:Dispatch函数里没有做cmdId的合法性校验,结果上层传了个非法cmdId进来,直接数组越界。从那以后,我每个Dispatch函数第一件事就是校验cmdId范围。
2.5 节点对象(DeviceNode)
节点对象是HDF设备树中的挂载点。它描述了设备在设备树中的位置、匹配信息、优先级等。
struct HdfDeviceNode {
struct HdfDeviceObject *device; // 关联的设备对象
struct HdfDeviceNode *parent; // 父节点
struct HdfDeviceNode *sibling; // 兄弟节点
uint32_t priority; // 加载优先级
const char *moduleName; // 匹配的驱动名称
const char *deviceName; // 设备名称
};
节点对象最重要的作用是驱动匹配。HDF启动时,会遍历设备树中的所有节点,根据moduleName找到对应的驱动,然后调用驱动的Bind和Init。
| 对象类型 | 核心作用 | 生命周期 | 关键回调/方法 |
|---|---|---|---|
| 驱动对象 | 描述驱动模块 | Bind → Init → Release | Bind, Init, Release |
| 设备对象 | 驱动与硬件的桥梁 | 随驱动创建/销毁 | service, priv |
| 服务对象 | 对外提供能力 | Init时注册,Release时注销 | Dispatch |
| 节点对象 | 设备树挂载点 | 系统启动时创建 | moduleName匹配 |
2.6 生命周期管理实战
光讲理论不够,我们来看一个实际的驱动模板。这是我写驱动时常用的骨架:
// my_driver.c
#include "hdf_device_desc.h"
#include "hdf_log.h"
#define HDF_LOG_TAG my_driver
// 私有数据结构
struct MyDriverPriv {
uint32_t regBase;
uint32_t irqNum;
};
// Bind:解析配置,分配私有数据
static int32_t MyDriverBind(struct HdfDeviceObject *device)
{
struct MyDriverPriv *priv = NULL;
// 解析HCS配置
// ... 从device->property中读取regBase和irqNum
priv = (struct MyDriverPriv *)OsalMemCalloc(sizeof(struct MyDriverPriv));
if (priv == NULL) {
HDF_LOGE("MyDriverBind: alloc priv failed");
return HDF_ERR_MALLOC_FAIL;
}
device->priv = (struct HdfObject *)priv;
HDF_LOGI("MyDriverBind: success");
return HDF_SUCCESS;
}
// Init:初始化硬件,注册服务
static int32_t MyDriverInit(struct HdfDeviceObject *device)
{
struct MyDriverPriv *priv = (struct MyDriverPriv *)device->priv;
// 初始化硬件寄存器
// ...
// 注册服务
device->service = &g_myService; // 全局服务对象
HDF_LOGI("MyDriverInit: success");
return HDF_SUCCESS;
}
// Release:释放资源
static void MyDriverRelease(struct HdfDeviceObject *device)
{
struct MyDriverPriv *priv = (struct MyDriverPriv *)device->priv;
if (priv != NULL) {
OsalMemFree(priv);
device->priv = NULL;
}
device->service = NULL;
HDF_LOGI("MyDriverRelease: success");
}
// 驱动入口
struct HdfDriverEntry g_myDriverEntry = {
.moduleVersion = 1,
.moduleName = "my_driver",
.Bind = MyDriverBind,
.Init = MyDriverInit,
.Release = MyDriverRelease,
};
HDF_INIT(g_myDriverEntry);
生命周期管理要点:
- Bind:只做资源分配和配置解析,不操作硬件。
- Init:操作硬件、注册中断、创建服务。
- Release:逆序释放Init和Bind分配的资源,防止内存泄漏。
2.7 避坑指南
做驱动开发这些年,我在生命周期管理上栽过不少跟头。分享几个典型的坑:
- Bind里操作硬件:设备还没上电,直接读写寄存器导致系统挂死。
- Release里忘记释放priv:驱动卸载后内存泄漏,反复加载卸载几次后系统OOM。
- Init失败后没有清理Bind分配的资源:导致部分资源泄漏。
- 多个设备共用一个驱动对象:没有区分device->priv,导致数据错乱。
我曾经遇到一个最隐蔽的bug:驱动Init成功,但上层调用Dispatch时发现service指针是空的。查了两天才发现,是HCS配置里设备节点和驱动名称不匹配,导致驱动虽然加载了,但设备对象没有正确关联到服务。从那以后,我每次写驱动都会在Init最后加一句日志打印service地址,确认服务注册成功。
2.8 小结
四个对象的关系,说白了就是一句话:驱动对象生设备,设备对象挂服务,节点对象管匹配。你把这个关系刻在脑子里,HDF的代码就不再是一团乱麻了。