3. HDF核心概念详解

驱动框架这东西,说白了就是一套标准化的“插拔”规则。你写驱动,本质上是在跟三个核心概念打交道:驱动对象、设备对象、服务接口。再加上一个贯穿始终的消息机制,这四个东西构成了HDF的骨架。

我记得刚接触HDF时,光看头文件里的结构体定义,感觉每个都差不多。后来踩了几个坑才明白——它们各管一摊,分工极其明确。今天我就把这四个概念掰开揉碎了讲给你听。

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

每个驱动在HDF里注册时,都需要一个HdfDriverEntry结构体。它告诉框架:你是谁、你从哪来、你要干什么。

struct HdfDriverEntry {
    uint16_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:驱动与设备对象建立关联。此时驱动还没真正“跑起来”,只是告诉框架“这个设备归我管”。
  • Init:真正的初始化工作。分配内存、注册中断、配置寄存器,都在这里做。
  • Release:驱动卸载时的清理工作。释放资源、注销服务,一个都不能少。

我个人习惯在Init里做大部分初始化,Bind只做最轻量的关联。为什么?因为Bind失败时框架会重试,如果里面放了太重的工作,重试成本就高了。

3.2 设备对象(HdfDeviceObject)——驱动的“工作台”

HdfDeviceObject是驱动与HDF框架交互的桥梁。每个驱动实例都对应一个设备对象,它承载了驱动的所有上下文信息。

struct HdfDeviceObject {
    struct IDeviceIoService  *service;       // 服务接口
    struct HdfDeviceObject  *parent;         // 父设备
    struct HdfDeviceObject  *children;       // 子设备链表
    uint32_t                 deviceId;       // 设备ID
    const char              *deviceName;     // 设备名称
    struct HdfObject         *object;        // 私有数据指针
};

这里有个关键字段——object。它是个void *类型的指针,你可以把驱动的私有数据挂在这里。比如你的驱动里有个结构体存了寄存器基址、中断号、缓冲区,统统挂到object上。

我的经验: 在Init里用HdfDeviceObject的object字段挂载私有数据,然后在Bind里通过HdfDeviceObject访问它。这样代码结构清晰,也方便调试。

3.3 服务接口(HdfIoService)——驱动的“对外窗口”

驱动写好了,上层应用怎么调用它?答案就是HdfIoService。它封装了驱动对外暴露的服务接口,用户态程序通过它来与内核态驱动通信。

struct HdfIoService {
    struct IDeviceIoService base;       // 基础服务接口
    struct HdfSession      *session;    // 会话对象
    void                   *priv;       // 私有数据
};

用户态获取服务的方式很简单:

struct HdfIoService *serv = HdfIoServiceBind("HDF_PLATFORM_I2C_0");
if (serv == NULL) {
    // 服务获取失败,处理错误
    return -1;
}
// 使用服务
serv->base.Dispatch(&serv->base, cmd, data, reply);
// 释放服务
HdfIoServiceRecycle(serv);

你想想看,这个Dispatch接口就是驱动的“命令分发器”。上层传一个命令码和数据下来,驱动根据命令码执行不同操作。我在项目中遇到过一个问题:多个线程同时调用Dispatch,导致驱动内部数据错乱。后来加了个自旋锁才解决。

注意: HdfIoServiceBind返回的服务对象,用完后必须调用HdfIoServiceRecycle释放。否则会造成资源泄漏,长时间运行的系统尤其要小心。

3.4 消息机制——驱动的“神经传导”

HDF的消息机制,说白了就是驱动之间、驱动与用户态之间传递数据的一套标准协议。它基于HdfMessage结构体实现。

struct HdfMessage {
    uint32_t        code;       // 消息码
    uint32_t        flags;      // 标志位
    uint32_t        dataSize;   // 数据大小
    const void     *data;       // 数据指针
    uint32_t        replySize;  // 回复数据大小
    void           *replyData;  // 回复数据指针
};

消息传递的典型流程是这样的:

  1. 发送方构造HdfMessage,填充消息码和数据。
  2. 调用HdfSendMessage发送消息。
  3. 接收方在Dispatch回调中解析消息码,执行对应操作。
  4. 如果需要回复,填充replyData和replySize。

嗯,这里要注意:消息机制支持同步和异步两种模式。同步模式下,发送方会阻塞等待回复;异步模式下,发送方发完就走,回复通过回调通知。我建议在驱动初始化阶段用同步模式,确保配置正确;在正常运行阶段用异步模式,避免阻塞影响性能。

3.5 核心关系一览

这四个概念的关系,我用一张图来总结:

HDF核心概念关系图 HdfDriverEntry 驱动对象(身份证) HdfDeviceObject 设备对象(工作台) HdfIoService 服务接口(窗口) HdfMessage 消息机制(神经) 生命周期管理 Bind → Init → Release 驱动对象管理设备对象 用户态通信 HdfIoServiceBind Dispatch 命令分发 消息机制贯穿上下 同步/异步消息传递,驱动间通信,用户态与内核态数据交换 消息码 + 数据指针 + 回复机制 = 标准通信协议

3.6 实战对比:四个概念的区别

概念 核心作用 生命周期 谁创建 典型场景
HdfDriverEntry 驱动注册与生命周期 驱动加载到卸载 驱动开发者 驱动初始化、资源释放
HdfDeviceObject 设备上下文与数据挂载 设备创建到销毁 HDF框架 存储私有数据、设备树
HdfIoService 用户态与内核态通信 服务绑定到释放 用户态程序 调用驱动功能、获取数据
HdfMessage 消息传递与命令分发 消息发送到回复 调用方 驱动间通信、命令交互

我曾经在一个项目中,把HdfDeviceObject和HdfDriverEntry搞混了,在Bind里试图访问HdfDeviceObject的service字段,结果发现它还是空的。后来才意识到——Bind阶段服务还没注册呢,得等到Init之后才能用。

避坑指南: 记住这个顺序——Bind建立关联,Init初始化资源,Release清理资源。服务接口在Init之后才可用。别在Bind里做需要服务接口的事情。

这四个概念,你掌握了,HDF驱动框架的骨架就搭起来了。后面写驱动时,你会反复跟它们打交道。多写几个demo,自然就熟了。