3. 传统HAL(libhardware)架构:hw_module_t与hw_device_t结构体详解,动态库加载机制

传统HAL(libhardware)是Android早期(至Android 8.0之前)的标准硬件抽象层实现。其核心设计围绕两个关键结构体——hw_module_t和hw_device_t展开,并通过动态库加载机制实现Framework与底层驱动的解耦。本章将深入剖析这两个结构体的内存布局、字段含义,以及HAL模块的加载与初始化流程。

3.1 hw_module_t:硬件模块的“身份证”

hw_module_t是每个HAL模块必须暴露的第一个结构体,它描述了模块的基本信息,并提供了打开设备实例的入口。该结构体定义在hardware/libhardware/include/hardware/hardware.h中。

核心要点:每个HAL共享库必须导出一个名为HAL_MODULE_INFO_SYM(即HMI)的hw_module_t实例,这是Framework加载器识别模块的唯一入口。
typedef struct hw_module_t {
    /** 标签,必须设置为 HARDWARE_MODULE_TAG */
    uint32_t tag;

    /** 模块主版本号 */
    uint16_t version_major;

    /** 模块次版本号 */
    uint16_t version_minor;

    /** 模块ID,用于标识模块类型,如 "audio"、"gps"、"lights" */
    const char *id;

    /** 模块名称(可读字符串) */
    const char *name;

    /** 模块作者/厂商信息 */
    const char *author;

    /** 模块自定义数据指针,通常用于扩展 */
    struct hw_module_methods_t* methods;

    /** 模块的dso句柄,由加载器填充,指向动态库的句柄 */
    void* dso;

    /** 预留字段,用于未来扩展,必须初始化为0 */
    uint32_t reserved[32-7];

} hw_module_t;
字段 类型 说明
tag uint32_t 必须为HARDWARE_MODULE_TAG(0x484D4F44),用于校验结构体有效性
version_major uint16_t 主版本号,不兼容的API变更时递增
version_minor uint16_t 次版本号,向后兼容的扩展时递增
id const char* 模块标识字符串,如"audio"、"gps",用于匹配加载
methods struct hw_module_methods_t* 指向模块方法表,目前仅包含open函数指针
dso void* 动态库句柄,由hw_get_module自动填充,模块自身不应修改

3.2 hw_device_t:硬件设备的“操作手柄”

hw_device_t代表一个具体的硬件设备实例。当调用hw_module_methods_t::open成功后,Framework会获得一个指向hw_device_t的指针,通过它来操作硬件。

typedef struct hw_device_t {
    /** 标签,必须设置为 HARDWARE_DEVICE_TAG */
    uint32_t tag;

    /** 设备版本号 */
    uint32_t version;

    /** 指向所属模块的指针 */
    struct hw_module_t* module;

    /** 预留字段,必须初始化为0 */
    uint32_t reserved[12];

    /** 关闭设备的函数指针 */
    int (*close)(struct hw_device_t* device);

} hw_device_t;
设计模式:hw_device_t采用“公共基类+私有扩展”的模式。每个具体的HAL(如Camera、Audio)都会在hw_device_t的基础上,通过结构体嵌套或指针转换,扩展出自己特有的操作函数和属性。

3.3 hw_module_methods_t:打开设备的唯一入口

hw_module_methods_t结构体目前只包含一个函数指针——open,它是从模块到设备实例的桥梁。

typedef struct hw_module_methods_t {
    /** 打开指定设备的方法 */
    int (*open)(const struct hw_module_t* module, const char* id,
                struct hw_device_t** device);
} hw_module_methods_t;
  • 参数module:指向当前模块的指针,由Framework传入。
  • 参数id:设备标识符,用于区分同一模块下的不同设备(如主摄像头与副摄像头)。
  • 参数device:输出参数,指向新创建的hw_device_t实例。
  • 返回值:0表示成功,负数表示错误码。

3.4 动态库加载机制:hw_get_module 流程

Framework通过hw_get_module函数加载HAL模块。该函数封装了动态库的查找、打开和符号解析过程。其核心流程如下:

  1. 确定模块路径:根据模块ID,在预定义的搜索路径列表中查找对应的.so文件。默认路径包括/vendor/lib/hw/、/system/lib/hw/等。
  2. 调用dlopen:使用dlopen打开共享库,获取句柄。
  3. 查找符号HMI:通过dlsym查找HAL_MODULE_INFO_SYM符号,即hw_module_t实例。
  4. 校验tag字段:验证hw_module_t的tag是否为HARDWARE_MODULE_TAG,防止加载错误模块。
  5. 填充dso字段:将dlopen返回的句柄赋值给hw_module_t::dso。
  6. 返回模块指针:将校验通过的hw_module_t指针返回给调用者。
兼容性注意:传统HAL的hw_get_module依赖固定的文件路径和命名规则(如audio.primary.<board>.so)。在Treble架构中,这一机制被HIDL服务注册和dlopen替代,但底层思想仍然延续。

3.5 结构体关系与加载流程SVG图

下图展示了hw_module_t、hw_device_t、hw_module_methods_t三者之间的关系,以及hw_get_module的加载流程:

传统HAL结构体关系与动态库加载流程 hw_module_t methods → hw_module_methods_t hw_module_methods_t open() 函数指针 hw_device_t 1. hw_get_module(id) 2. 查找 .so 文件路径 3. dlopen() 加载库 4. dlsym(HMI) 找模块 5. 校验 tag 字段 6. 返回 hw_module_t* 关键命名约定 <type>.<variant> .<target>.so 例: audio.primary .sdm845.so HMI 宏定义 #define HAL_MODULE _INFO_SYM HMI

3.6 典型HAL模块实现示例

以下是一个虚拟“LED”HAL模块的简化实现,展示了hw_module_t和hw_device_t的典型用法:

/* led_hal.c - 虚拟LED HAL模块实现 */

/* 定义LED设备特有的结构体,继承自hw_device_t */
struct led_device_t {
    struct hw_device_t common;  /* 基类必须放在第一个字段 */

    /* LED特有操作 */
    int (*set_on)(struct led_device_t* dev, int on);
    int (*set_brightness)(struct led_device_t* dev, int level);
};

/* 打开设备函数 */
static int led_open(const struct hw_module_t* module, const char* id,
                    struct hw_device_t** device) {
    struct led_device_t* dev = malloc(sizeof(struct led_device_t));
    memset(dev, 0, sizeof(*dev));

    /* 填充基类 */
    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = 1;
    dev->common.module = (struct hw_module_t*)module;
    dev->common.close = led_close;

    /* 填充特有操作 */
    dev->set_on = led_set_on_impl;
    dev->set_brightness = led_set_brightness_impl;

    *device = (struct hw_device_t*)dev;
    return 0;
}

/* 模块方法表 */
static struct hw_module_methods_t led_methods = {
    .open = led_open,
};

/* 模块实例 - 必须导出为 HMI 符号 */
struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .version_major = 1,
    .version_minor = 0,
    .id = "led",
    .name = "Virtual LED HAL",
    .author = "Embedded Expert",
    .methods = &led_methods,
    .dso = NULL,  /* 由加载器填充 */
};
关键点回顾:
  • hw_module_t是模块的静态描述,每个共享库只有一个实例。
  • hw_device_t是设备实例,每次open调用可能创建多个实例(如多个摄像头)。
  • 动态库加载通过dlopen/dlsym实现,hw_get_module封装了查找和校验逻辑。
  • 具体HAL通过结构体嵌套扩展hw_device_t,实现多态效果。