3、HDF框架核心概念:驱动对象、驱动服务、驱动配置、驱动消息机制

好,咱们今天来啃HDF框架的四个核心概念。说实话,这四个东西刚接触时容易搞混,但一旦理清了,整个驱动开发的脉络就通了。我当年第一次看HDF源码时,也是对着这四个概念反复琢磨,才慢慢摸到门道。

驱动对象、驱动服务、驱动配置、驱动消息机制——它们分别回答了四个问题:“谁在干活?”、“能提供什么?”、“怎么配置它?”、“怎么跟它通信?”。咱们一个一个来拆。

3.1 驱动对象(Driver Object)——驱动的“身份证”

驱动对象,说白了就是HDF框架里每个驱动的“户口本”。它告诉框架:“我是谁,我属于哪个设备,我该怎么被初始化”

在代码里,驱动对象是一个结构体:

struct HdfDriverEntry {
    uint32_t moduleVersion;
    const char *moduleName;
    int32_t (*Bind)(struct HdfDeviceObject *deviceObject);
    int32_t (*Init)(struct HdfDeviceObject *deviceObject);
    void (*Release)(struct HdfDeviceObject *deviceObject);
};

嗯,这里要注意:BindInitRelease这三个函数指针,就是驱动的生命周期回调。框架会在不同阶段调用它们:

  • Bind:驱动绑定到设备时调用。我习惯在这里做资源预分配,比如申请内存、注册服务接口。
  • Init:驱动初始化时调用。真正的硬件初始化逻辑放这里,比如配置寄存器、启动中断。
  • Release:驱动卸载时调用。别忘了释放资源,我曾经漏掉一个定时器没释放,结果系统重启时一直报错。
我的习惯:Bind里只做“轻量级”工作,比如注册服务。Init里做“重量级”工作,比如硬件初始化。这样如果Init失败,Bind已经注册的服务还能被其他模块优雅处理。

3.2 驱动服务(Driver Service)——驱动的“能力清单”

驱动对象是“你是谁”,驱动服务就是“你能干什么”。每个驱动可以对外暴露一组服务接口,供其他模块调用。

举个例子,你写了一个I2C驱动,那它提供的服务可能就是:I2cRead()I2cWrite()I2cTransfer()。其他模块(比如传感器驱动)通过HDF框架找到这个服务,然后直接调用。

注册服务的代码大概长这样:

int32_t MyDriverBind(struct HdfDeviceObject *deviceObject) {
    static struct I2cDriverService service;
    service.i2cRead = MyI2cRead;
    service.i2cWrite = MyI2cWrite;
    // 注册到HDF服务管理器
    HdfDeviceRegisterService(deviceObject, &service);
    return HDF_SUCCESS;
}

你想想看,如果没有驱动服务机制,每个驱动都得自己搞一套“接口暴露”的方法,那整个系统就乱套了。HDF框架统一了这件事,所有服务都通过服务管理器来查找和调用。

核心要点:驱动服务是HDF实现“驱动解耦”的关键。上层应用不需要知道底层硬件细节,只需要通过服务名找到对应的驱动服务,然后调用接口就行。

3.3 驱动配置(Driver Configuration)——驱动的“说明书”

驱动配置,就是告诉驱动“你该用哪个GPIO、I2C地址是多少、中断号是什么”。这些信息通常写在HCS(HDF Configuration Source)配置文件中。

HCS文件是HDF框架的“灵魂”之一。它用树形结构描述设备信息,比如:

root {
    device_sample {
        device0 :: device {
            deviceInfo {
                policy = 2;
                priority = 100;
                preload = 0;
                permission = 0664;
                moduleName = "HDF_SAMPLE";
                serviceName = "sample_service";
                deviceMatchAttr = "sample_config";
            }
        }
    }
}

这里每个字段都有讲究:

字段 含义 我的建议
policy 服务发布策略(0不发布,1对内核发布,2对用户态发布) 调试阶段用2,发布前改成1
priority 驱动加载优先级(数值越小越优先) 依赖其他驱动的驱动,优先级要设高(数值小)
preload 是否预加载(0不预加载,1预加载) 系统关键驱动建议预加载
deviceMatchAttr 匹配的设备属性名 这个名字要和驱动代码里的一致,否则绑定不上
避坑指南:我曾经因为deviceMatchAttr写错了一个字母,结果驱动死活加载不上。排查了半天才发现是配置文件和代码里的名字不匹配。所以,配置文件和驱动代码里的名字一定要严格一致

3.4 驱动消息机制(Driver Message)——驱动的“通信管道”

最后一个概念,驱动消息机制。它解决的是“驱动之间怎么通信”的问题。

在HDF框架里,驱动消息机制基于消息队列实现。一个驱动可以向另一个驱动发送消息,消息可以是同步的,也可以是异步的。

发送消息的接口很简单:

int32_t HdfDeviceSendEvent(
    struct HdfDeviceObject *deviceObject,
    uint32_t eventId,
    struct HdfSBuf *data
);

接收消息的驱动需要注册一个回调:

int32_t MyDriverEventReceiver(
    struct HdfDeviceObject *deviceObject,
    uint32_t eventId,
    struct HdfSBuf *data
) {
    // 处理消息
    return HDF_SUCCESS;
}

为什么需要消息机制?你想想看,驱动之间有时候需要“打招呼”。比如电源管理驱动通知外设驱动“系统要休眠了”,或者传感器驱动通知上层“数据准备好了”。如果没有统一的消息机制,每个驱动都得自己搞一套通信方式,那维护起来就是噩梦。

我的经验:消息机制特别适合“事件通知”场景。比如按键驱动检测到按键按下,通过消息通知上层应用。这种场景用消息机制比用轮询高效得多,而且代码更清晰。

3.5 四个概念的关系——一张图看懂

说了这么多,咱们用一张图把四个概念串起来:

HDF框架四大核心概念关系图 驱动对象 (HdfDriverEntry) 驱动服务 (能力接口) 驱动配置 (HCS配置文件) 驱动消息机制 (事件通信) 注册 加载 使用 事件通知 配置参数 驱动对象是核心,驱动服务提供能力,驱动配置决定行为,驱动消息机制实现通信

从这张图可以看得很清楚:驱动对象是核心,它通过配置加载,注册服务,并使用消息机制与其他驱动通信。这四个概念缺一不可,共同构成了HDF驱动框架的基石。

好了,这一章的内容就到这里。记住这四个概念,后面咱们写驱动时,每一步都会跟它们打交道。


专注资料整理