4、驱动入口与初始化:驱动入口函数实现、Bind/Init/Release接口、驱动加载流程
驱动入口,说白了就是操作系统认识你这个驱动的第一道门。鸿蒙的HDF框架里,这个门长什么样?怎么开?开完之后干什么?今天咱们就把这事彻底聊透。
我个人习惯,写驱动之前先想清楚三件事:什么时候加载?加载后做什么?卸载时怎么打扫干净? 这三个问题对应到HDF里,就是Bind、Init、Release三个接口。嗯,咱们一个一个来看。
4.1 驱动入口函数:从哪开始?
每个HDF驱动都必须有一个入口描述结构。这个结构告诉框架:你是谁,你提供什么能力,你的生命周期回调在哪。
先看一个最简的入口定义:
// 驱动入口结构
struct HdfDriverEntry g_sampleDriverEntry = {
.moduleVersion = 1,
.moduleName = "sample_driver",
.Bind = SampleDriverBind,
.Init = SampleDriverInit,
.Release = SampleDriverRelease,
};
// 必须用这个宏注册
HDF_INIT(g_sampleDriverEntry);
这里有个关键点——HDF_INIT 宏。它会把你的驱动入口放到特定的段里,HDF框架启动时会扫描这个段,找到所有驱动。我曾经见过有人忘了加这个宏,结果驱动死活加载不上,查了两天才发现是这问题。
HdfDriverEntry 结构,并且必须用 HDF_INIT 注册。这是框架识别驱动的唯一入口。
4.2 Bind接口:驱动与设备的绑定
Bind是驱动加载的第一个回调。它的职责很简单:把你的驱动对象挂到设备对象上。
说白了,就是牵线搭桥。驱动是个服务提供者,设备是个硬件实体,Bind就是把它们俩绑在一起。
static int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject)
{
// 驱动私有数据结构
struct SampleDriverData *driverData = NULL;
if (deviceObject == NULL) {
HDF_LOGE("SampleDriverBind: deviceObject is NULL");
return HDF_ERR_INVALID_PARAM;
}
// 分配驱动私有数据
driverData = (struct SampleDriverData *)OsalMemAlloc(sizeof(struct SampleDriverData));
if (driverData == NULL) {
HDF_LOGE("SampleDriverBind: failed to alloc driver data");
return HDF_ERR_MALLOC_FAIL;
}
// 初始化驱动数据
driverData->deviceIndex = 0;
driverData->regBase = 0x20000000;
driverData->irqNum = 42;
// 挂到设备对象上
deviceObject->service = driverData;
HDF_LOGI("SampleDriverBind: success");
return HDF_SUCCESS;
}
注意看最后一行——deviceObject->service = driverData。这就是绑定的本质。以后框架或者其他服务想访问你的驱动,通过这个service指针就能找到你的私有数据。
4.3 Init接口:驱动真正的初始化
Bind做完之后,框架会调用Init。这才是驱动真正开始干活的地方。
Init里通常做三件事:
- 硬件初始化——配置寄存器、使能时钟、设置中断
- 注册服务——把驱动能力暴露给上层
- 创建设备节点——让用户态能访问
static int32_t SampleDriverInit(struct HdfDeviceObject *deviceObject)
{
struct SampleDriverData *driverData = NULL;
if (deviceObject == NULL || deviceObject->service == NULL) {
HDF_LOGE("SampleDriverInit: invalid param");
return HDF_ERR_INVALID_PARAM;
}
driverData = (struct SampleDriverData *)deviceObject->service;
// 1. 硬件初始化:映射寄存器地址
driverData->regBaseVir = OsalIoRemap(driverData->regBase, 0x1000);
if (driverData->regBaseVir == NULL) {
HDF_LOGE("SampleDriverInit: IoRemap failed");
return HDF_ERR_IO;
}
// 2. 配置硬件寄存器(示例:使能设备)
uint32_t regVal = OSAL_READL(driverData->regBaseVir);
regVal |= 0x01; // 使能位
OSAL_WRITEL(regVal, driverData->regBaseVir);
// 3. 注册中断
int32_t ret = OsalRegisterIrq(driverData->irqNum, OSAL_IRQF_TRIGGER_HIGH,
SampleDriverIrqHandler, "sample_driver", driverData);
if (ret != HDF_SUCCESS) {
HDF_LOGE("SampleDriverInit: register irq failed");
return ret;
}
HDF_LOGI("SampleDriverInit: success");
return HDF_SUCCESS;
}
这里有个细节——deviceObject->service 在Bind里已经被赋值了,Init里直接拿过来用就行。你想想看,如果Bind没做好,Init里拿到的service就是NULL,那整个驱动就崩了。所以Bind和Init的配合很重要。
4.4 Release接口:优雅地退出
驱动卸载时,Release会被调用。它的任务就是把Bind和Init里分配的资源全部释放。
static void SampleDriverRelease(struct HdfDeviceObject *deviceObject)
{
struct SampleDriverData *driverData = NULL;
if (deviceObject == NULL || deviceObject->service == NULL) {
HDF_LOGE("SampleDriverRelease: invalid param");
return;
}
driverData = (struct SampleDriverData *)deviceObject->service;
// 1. 注销中断
OsalUnregisterIrq(driverData->irqNum, driverData);
// 2. 取消寄存器映射
if (driverData->regBaseVir != NULL) {
OsalIoUnmap(driverData->regBaseVir);
driverData->regBaseVir = NULL;
}
// 3. 释放私有数据
OsalMemFree(driverData);
deviceObject->service = NULL;
HDF_LOGI("SampleDriverRelease: success");
}
Release里释放的顺序要和Init里分配的顺序相反。这是基本原则——后分配的先释放,避免出现悬空指针。
4.5 驱动加载流程:从入口到就绪
现在我们把三个接口串起来,看看HDF框架加载驱动的完整流程。
这个流程其实很清晰。我总结一下关键点:
| 阶段 | 调用接口 | 主要工作 | 失败后果 |
|---|---|---|---|
| 1. 框架启动 | 无 | 扫描HDF_INIT段,收集所有驱动入口 | 驱动不被识别 |
| 2. 绑定阶段 | Bind | 分配私有数据,挂接到设备对象 | 驱动加载终止 |
| 3. 初始化阶段 | Init | 硬件初始化,注册中断和服务 | 驱动不可用,但已加载 |
| 4. 运行阶段 | 无 | 提供驱动能力给上层 | — |
| 5. 卸载阶段 | Release | 释放所有资源 | 资源泄漏 |
4.6 实战中的几个坑
最后分享几个我踩过的坑,希望能帮你少走弯路。
- Bind返回成功但Init失败: 这种情况下驱动已经被加载了,但不可用。上层调用时可能拿到一个半残的对象。我的做法是在Init失败时,主动调用Release清理资源,然后返回错误。
- Release里忘记置空指针: 释放内存后,记得把指针置NULL。否则如果其他地方还持有这个指针,就成了悬空指针,随时可能崩溃。
- 多个驱动共用一个入口: 有些场景下,一个驱动要管理多个同类型设备。这时候Bind里要根据设备ID分配不同的私有数据。我习惯在Bind里加一个设备索引判断,避免数据错乱。
驱动入口和初始化,说白了就是三板斧:Bind搭架子,Init填内容,Release拆干净。把这套流程吃透了,HDF驱动开发就算入门了。