第3章 第一个HDF驱动:hello_world
说实话,学驱动开发最怕什么?就是环境搭好了,却不知道第一个程序怎么写。我当年刚接触HDF时也懵,文档翻来翻去,就是找不到一个能直接跑起来的例子。所以这一章,咱们直接动手,写一个最简单的HDF驱动——hello_world。
这个驱动不干别的,就是加载时打印一句"Hello, HDF!",卸载时再打印一句"Goodbye, HDF!"。别看它简单,它把HDF驱动的骨架全走了一遍:入口函数、绑定、初始化、释放。你把这个跑通了,后面写任何复杂驱动心里都有底。
3.1 HDF驱动的“三件套”
每个HDF驱动都必须实现三个函数:Bind、Init、Release。我习惯叫它“三件套”。
| 函数 | 调用时机 | 主要职责 |
|---|---|---|
Bind |
驱动加载时,最早被调用 | 绑定服务接口,注册设备对象 |
Init |
Bind成功后调用 |
初始化硬件、分配资源、配置参数 |
Release |
驱动卸载时调用 | 释放资源、反初始化、清理现场 |
调用顺序是固定的:Bind → Init → Release。你想想看,这跟Linux驱动的probe/remove是不是很像?但HDF把Bind单独拎出来,就是为了让服务注册和硬件初始化解耦。我在项目中遇到过一个问题:某个传感器驱动在Init里申请了中断,但Bind还没注册完服务,结果中断来了找不到处理函数。后来我把服务注册放Bind里,硬件初始化放Init里,问题就解决了。
3.2 编写hello_world驱动源码
好,咱们直接上代码。新建一个文件 hello_world_driver.c,放在 drivers/hdf_core/adapter/khdf/linux/platform/ 下(或者你自己建的驱动目录里)。
#include "hdf_device_desc.h"
#include "hdf_log.h"
#define HDF_LOG_TAG hello_world_driver
// Bind函数:注册服务接口
static int32_t HelloWorldBind(struct HdfDeviceObject *deviceObject)
{
if (deviceObject == NULL) {
HDF_LOGE("HelloWorldBind: deviceObject is NULL");
return HDF_ERR_INVALID_PARAM;
}
HDF_LOGI("HelloWorldBind: enter");
// 这里可以注册服务,比如 deviceObject->service = &g_helloService;
return HDF_SUCCESS;
}
// Init函数:初始化驱动
static int32_t HelloWorldInit(struct HdfDeviceObject *deviceObject)
{
if (deviceObject == NULL) {
HDF_LOGE("HelloWorldInit: deviceObject is NULL");
return HDF_ERR_INVALID_PARAM;
}
HDF_LOGI("HelloWorldInit: Hello, HDF!");
return HDF_SUCCESS;
}
// Release函数:释放驱动
static void HelloWorldRelease(struct HdfDeviceObject *deviceObject)
{
if (deviceObject == NULL) {
HDF_LOGE("HelloWorldRelease: deviceObject is NULL");
return;
}
HDF_LOGI("HelloWorldRelease: Goodbye, HDF!");
}
// 驱动入口结构体
struct HdfDriverEntry g_helloWorldEntry = {
.moduleVersion = 1,
.moduleName = "hello_world",
.Bind = HelloWorldBind,
.Init = HelloWorldInit,
.Release = HelloWorldRelease,
};
// 注册驱动
HDF_INIT(g_helloWorldEntry);
代码不长,我带你捋一遍:
- Bind:检查参数,打印日志。实际项目中这里会调用
HdfDeviceObjectRegisterService注册服务。 - Init:打印"Hello, HDF!"。这就是咱们驱动的“第一声啼哭”。
- Release:打印"Goodbye, HDF!"。卸载时清理用。
- HdfDriverEntry:驱动入口结构体,告诉HDF框架你的驱动叫什么名字、三个函数在哪。
- HDF_INIT:宏,把驱动注册到HDF框架里。
HDF_LOGI 打印的日志可以通过 hilog 命令查看。调试时多用日志,比猜变量值靠谱多了。
3.3 配置驱动:HCS文件
源码写完了,但HDF框架还不知道你的驱动存在。你需要写一个HCS(HDF Configuration Source)配置文件,告诉框架:有个叫"hello_world"的驱动,它应该在哪个设备上加载。
新建 hello_world_config.hcs,放在 vendor/你的厂商/你的产品/hdf_config/ 下:
root {
module = "hello_world";
moduleConfig {
policy = 2; // 服务发布策略:0不发布,1发布到内核态,2发布到用户态
priority = 100; // 加载优先级,数值越小越优先
preload = 0; // 0表示按需加载,1表示系统启动时加载
deviceList = list {
device0 {
deviceName = "hello_world_device";
deviceNodeName = "HDF_PLATFORM";
deviceMatchAttr = "hello_world_config";
serviceName = "hello_world_service";
};
};
};
}
这里有几个字段我解释一下:
- policy:服务发布策略。咱们这个demo用2,表示用户态也能访问。
- priority:加载优先级。100是中间值,一般够用。
- preload:是否预加载。0表示按需加载,系统启动时不自动加载。
- deviceMatchAttr:匹配属性,HDF框架通过这个字段把配置和驱动代码关联起来。
嗯,这里要注意:moduleName 必须和驱动源码里 HdfDriverEntry 的 moduleName 一致。我见过有人把名字写错了,结果驱动死活加载不上,查了半天才发现是大小写问题。
3.4 编译与加载验证
配置写好了,接下来编译。在源码根目录执行:
hb build -f
编译成功后,把镜像烧录到开发板。然后通过串口或SSH登录,执行以下命令加载驱动:
# 加载驱动
hdf_loader load hello_world
# 查看驱动是否加载成功
hdf_loader list
# 卸载驱动
hdf_loader unload hello_world
加载成功后,用 hilog 查看日志:
hilog | grep hello_world
你应该能看到类似这样的输出:
01-01 00:00:01.123 456 789 I 00100/hello_world_driver: HelloWorldBind: enter
01-01 00:00:01.124 456 789 I 00100/hello_world_driver: HelloWorldInit: Hello, HDF!
卸载时:
01-01 00:00:02.456 456 789 I 00100/hello_world_driver: HelloWorldRelease: Goodbye, HDF!
HDF_LOG_TAG 定义错了。这个宏必须放在所有 HDF_LOGI 之前,而且名字不能有空格。另外,如果 hilog 过滤不到日志,试试不加 grep,先确认 hilog 本身能正常工作。
3.5 驱动生命周期流程图
为了让你更直观地理解HDF驱动的加载到卸载全过程,我画了一张流程图:
从图上可以清楚看到:系统启动后,HDF框架先初始化,然后加载驱动,依次调用Bind和Init。卸载时调用Release。这个流程是固定的,你写任何HDF驱动都得遵循。
3.6 常见问题与排查
第一次跑这个demo,大概率会遇到问题。我把常见的坑列出来:
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 驱动加载失败,提示"module not found" | HCS配置中的moduleName与源码不一致 | 检查 moduleName 是否匹配 |
| 日志不打印 | HDF_LOG_TAG 定义错误或hilog未开启 |
确认宏定义位置,检查hilog服务状态 |
| 驱动加载后立即崩溃 | Bind或Init中访问了空指针 | 加空指针检查,用 HDF_LOGE 定位 |
| 卸载时卡死 | Release中未正确释放资源 | 检查是否有未释放的锁或内存 |
好了,到这里你已经完成了第一个HDF驱动的编写、配置、编译和验证。别看它简单,这个hello_world驱动麻雀虽小五脏俱全。后面你要写的GPIO驱动、I2C驱动、SPI驱动,都是在这个骨架基础上加硬件操作而已。把这个跑通了,HDF驱动开发的大门就算正式打开了。