HAL 概述

基于Android P版本分析

HAL 概述

硬件抽象层(HAL) 是介于android framework 和Linux 内核之间抽象出来的一种结构,它是对Linux 驱动的一种封装,对上层提供统一的 接口,上层应用不需要知道下层硬件是如何实现的,屏蔽了底层实现的细节。

通过HAL 层,android 分两层来支持硬件驱动,其中一层在用户空间,另外一层在内核空间中,传统的linux 系统是将对硬件的支持和管理全 部放在内核空间中,对不同厂商的硬件支持全部放在硬件驱动模块之中。

本篇内容涉及到的具体的分析,我们都以audio模块为例来说明

HAL层框架分析

首先我们需要关注几个重要的结构体定义,要实现一个Android的HAL,需要实现下面这三个结构体;

  • hw_module_t
  • hw_device_t
  • hw_module_methods_t

这3个结构体定义都在hardware目录下的hardware.h中;

hw_module_t

用来描述硬件模块,hw_module_t是所有特定硬件模块的父类,例如对于audio、camera、sensor这些模块,他们不会直接使用hw_module_t结构体,而是会根据实际的情况将hw_module_t封装成一个新的结构体定义;

hw_module_t 结构体

arduino 复制代码
/**
 * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
 * and the fields of this data structure must begin with hw_module_t
 * followed by module specific information.
 */
typedef struct hw_module_t {
    uint32_t tag;
​
    // 模块 API 版本号
    uint16_t module_api_version;
    // 主版本号
#define version_major module_api_version
    
    // HAL层 API 版本号
    uint16_t hal_api_version;
    // 次版本号
#define version_minor hal_api_version
​
    // 模块ID,用来区别其他硬件模块
    /** Identifier of module */
    const char *id;
​
    // 模块名
    /** Name of this module */
    const char *name;
​
    // 作者
    /** Author/owner/implementor of the module */
    const char *author;
​
    // 硬件模块方法结构体,标识了硬件模块所包含的方法集
    /** Modules methods */
    struct hw_module_methods_t* methods;
​
    // 加开硬件模块的库时得到的句柄,HAL层中的硬件模块是用动态链接库表示的,dso指针就是系统使用dlopen函数打开动态共享链接库获得的句柄
    /** module's dso */
    void* dso;
​
#ifdef __LP64__
    uint64_t reserved[32-7];
#else
    /** padding to 128 bytes, reserved for future use */
    uint32_t reserved[32-7];
#endif
​
} hw_module_t;

在这个结构体的最前面有一段注释,含义为:

每一个硬件模块中都要定义一个名为HAL_MODULE_INFO_SYM的结构体变量,而这个结构体变量中的第一个成员必须是hw_module_t类型;

即每一个硬件模块都需要自己自定义一个模块结构体,其中该结构体中的第一个变量类型为hw_module_t;其本质上就是一种继承的思想,hw_module_t 是一个基类,描述所有硬件模块都应该具有的一些属性,具体到某个具体的硬件模块实现时,都需要继承hw_module_t 结构,也就是说hw_module_t 是所有特定硬件模块的父类。

hw_module_t id属性

在Android HAL的API定义中,我们关注hardware.h中定义的API接口,一共提供了两个:

arduino 复制代码
int hw_get_module(const char *id, const struct hw_module_t **module);
int hw_get_module_by_class(const char *class_id, const char *inst, const struct hw_module_t **module);

在hardware.c中实现了这两个函数,其本质上hw_get_module函数的实现依赖hw_get_module_by_class,通过参数区别;

这两个函数的核心都是通过id值获取对应的module,id值作为该module的唯一标识与module一一映射;

我们看一下常见的一些module ID:

arduino 复制代码
#define SENSORS_HARDWARE_MODULE_ID "sensors"
#define AUDIO_HARDWARE_MODULE_ID "audio"
#define RADIO_HARDWARE_MODULE_ID "radio" 
#define RADIO_HARDWARE_MODULE_ID_FM "amfm" /* corresponds to RADIO_CLASS_AM_FM */
#define CAMERA_HARDWARE_MODULE_ID "camera"
#define GPS_HARDWARE_MODULE_ID "gps"
#define NFC_TAG_HARDWARE_MODULE_ID "nfc_tag"
#define AUDIO_HARDWARE_MODULE_ID_PRIMARY "primary" 
#define AUDIO_HARDWARE_MODULE_ID_A2DP "a2dp"
#define AUDIO_HARDWARE_MODULE_ID_USB "usb"
#define AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX "r_submix"
#define BT_HARDWARE_MODULE_ID "bluetooth"
..................

这些module ID值都定义在各个模块的头文件中;

将指定的module ID值传入到了hw_get_module函数中,最终module指针就可以指向对应的module了,后续通过该module指针来open对应的模块等操作;

audio实例

audio_module 定义 & 初始化
arduino 复制代码
struct audio_module {
    struct hw_module_t common;
};

在hardware目录的audio.h文件中定义了audio_module结构体,该结构体中封装了hw_module_t结构体,audio_module结构体属于简单的封装,其他的模块的module结构体定义有属于复杂的封装;

ini 复制代码
struct audio_module HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = AUDIO_MODULE_API_VERSION_0_1,
        .hal_api_version = HARDWARE_HAL_API_VERSION,
        .id = AUDIO_HARDWARE_MODULE_ID,
        .name = "QCOM Audio HAL",
        .author = "The Linux Foundation",
        .methods = &hal_module_methods,
    },
};

在audio.h中定义好了audio_module结构体之后,在后续的使用中就需要对该结构体进行赋值了;具体的赋值位置在各个厂商自定义的目录中,一般情况下可以在vendor目录中查找;

上述的一系列定义印证了HAL_MODULE_INFO_SYM的概念;

在初始化common参数的时候,将id值赋值为了AUDIO_HARDWARE_MODULE_ID,即后续操作的module为AUDIO_HARDWARE_MODULE;

audio_module 加载
ini 复制代码
// static
int DevicesFactory::loadAudioInterface(const char* if_name, audio_hw_device_t** dev) {
    const hw_module_t* mod;
    int rc;
​
    rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
    if (rc) {
        ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID,
              if_name, strerror(-rc));
        goto out;
    }
    rc = audio_hw_device_open(mod, dev);
    if (rc) {
        ALOGE("%s couldn't open audio hw device in %s.%s (%s)", __func__, AUDIO_HARDWARE_MODULE_ID,
              if_name, strerror(-rc));
        goto out;
    }
    if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) {
        ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version);
        rc = -EINVAL;
        audio_hw_device_close(*dev);
        goto out;
    }
    return OK;
​
out:
    *dev = NULL;
    return rc;
}

而在audio hal模块进行加载时,首先调用了hw_get_module_by_class函数,传入了AUDIO_HARDWARE_MODULE_ID加载执行的module,module加载完成之后,调用audio_hw_device_open函数开启module中执行的device,上述的流程在AudioPolicyManager的loadHwModule中实现;

hw_module_methods_t

用来打开硬件模块中包含的硬件设备,获得指向硬件设备结构体的指针;

我们首先先描述一下hw_module_methods_t结构体,因为这个结构体中定义的open函数会关联到hw_device_t模块的内容;

hw_module_methods_t 结构体

arduino 复制代码
typedef struct hw_module_methods_t {
    /** Open a specific device */
    int (*open)(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device);
​
} hw_module_methods_t;

这个结构体非常简单,其中只定义了一个open函数指针,在open函数中传入了module参数和device参数,id为常量值AUDIO_HARDWARE_INTERFACE,用来打开hw_deivce_t硬件设备;

audio 实例

module获取成功之后,紧接着就是调用audio_hw_device_open函数开启指定的device;

arduino 复制代码
static inline int audio_hw_device_open(const struct hw_module_t* module,
                                       struct audio_hw_device** device)
{
    return module->methods->open(module, AUDIO_HARDWARE_INTERFACE,
                                 TO_HW_DEVICE_T_OPEN(device));
}

在audio_hw_device_open函数中,其实调用的就是module结构体中的hw_module_methods_t结构体中定义的open函数,而open函数的实现在audio_hw.c中定义;

open函数赋值
ini 复制代码
static struct hw_module_methods_t hal_module_methods = {
    .open = adev_open,
};

我们在audio_hw.c中初始化audio_module结构体的时候,将其中的common变量进行了赋值,其中包含了对hw_module_methods_t结构体的赋值,而该值就是hal_module_methods。所以在初始化audio_module的时候,open函数指针就已经定义好了,该open函数指针指向了adev_open函数,该函数定义在audio_hw.c中;

hw_device_t

用来描述硬件设备,其实和hw_module_t类似,hw_device_t也可以看做是一个基类,它描述了所有硬件设备都应该具有的属性,然后具体到某个特定的硬件设备实现时,都需要封装自己的结构体。所以每个HAL层中硬件设备而对应的结构体中的第一个成员必须是hw_device_t;

hw_device_t 结构体

arduino 复制代码
/**
 * Every device data structure must begin with hw_device_t
 * followed by module specific public methods and attributes.
 */
typedef struct hw_device_t {
    /** tag must be initialized to HARDWARE_DEVICE_TAG */
    uint32_t tag;
​
    // 版本
    uint32_t version;
​
    // 表明该device从属于的module
    /** reference to the module this device belongs to */
    struct hw_module_t* module;
​
    /** padding reserved for future use */
#ifdef __LP64__
    uint64_t reserved[12];
#else
    uint32_t reserved[12];
#endif
​
    // 关闭设备的函数指针
    /** Close this device */
    int (*close)(struct hw_device_t* device);
​
} hw_device_t;
  • module变量表示该hw_device_t(硬件设备)是由哪个hw_module_t(硬件模块)进行管理;
  • close:这是一个函数指针,表示关闭已打开的硬件设备,这个对应这open指针函数,open指针函数定义在hw_module_methods_t结构体中;

audio 实例

audio_hw_device 定义
rust 复制代码
struct audio_hw_device {
​
    struct hw_device_t common;
​
    uint32_t (*get_supported_devices)(const struct audio_hw_device *dev);
​
    int (*init_check)(const struct audio_hw_device *dev);
​
    /** set the audio volume of a voice call. Range is between 0.0 and 1.0 */
    int (*set_voice_volume)(struct audio_hw_device *dev, float volume);
​
    int (*set_master_volume)(struct audio_hw_device *dev, float volume);
​
    int (*get_master_volume)(struct audio_hw_device *dev, float *volume);
​
    int (*set_mode)(struct audio_hw_device *dev, audio_mode_t mode);
​
    /* mic mute */
    int (*set_mic_mute)(struct audio_hw_device *dev, bool state);
    int (*get_mic_mute)(const struct audio_hw_device *dev, bool *state);
​
    /* set/get global audio parameters */
    int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs);
​
    char * (*get_parameters)(const struct audio_hw_device *dev,
                             const char *keys);
​
    size_t (*get_input_buffer_size)(const struct audio_hw_device *dev,
                                    const struct audio_config *config);
​
    int (*open_output_stream)(struct audio_hw_device *dev,
                              audio_io_handle_t handle,
                              audio_devices_t devices,
                              audio_output_flags_t flags,
                              struct audio_config *config,
                              struct audio_stream_out **stream_out,
                              const char *address);
​
    void (*close_output_stream)(struct audio_hw_device *dev,
                                struct audio_stream_out* stream_out);
​
    /** This method creates and opens the audio hardware input stream */
    int (*open_input_stream)(struct audio_hw_device *dev,
                             audio_io_handle_t handle,
                             audio_devices_t devices,
                             struct audio_config *config,
                             struct audio_stream_in **stream_in,
                             audio_input_flags_t flags,
                             const char *address,
                             audio_source_t source);
​
    void (*close_input_stream)(struct audio_hw_device *dev,
                               struct audio_stream_in *stream_in);
​
    int (*get_microphones)(const struct audio_hw_device *dev,
                           struct audio_microphone_characteristic_t *mic_array,
                           size_t *mic_count);
​
    /** This method dumps the state of the audio hardware */
    int (*dump)(const struct audio_hw_device *dev, int fd);
​
    int (*set_master_mute)(struct audio_hw_device *dev, bool mute);
​
    int (*get_master_mute)(struct audio_hw_device *dev, bool *mute);
​
    int (*create_audio_patch)(struct audio_hw_device *dev,
                               unsigned int num_sources,
                               const struct audio_port_config *sources,
                               unsigned int num_sinks,
                               const struct audio_port_config *sinks,
                               audio_patch_handle_t *handle);
​
    /* Release an audio patch */
    int (*release_audio_patch)(struct audio_hw_device *dev,
                               audio_patch_handle_t handle);
​
    int (*get_audio_port)(struct audio_hw_device *dev,
                          struct audio_port *port);
​
    /* Set audio port configuration */
    int (*set_audio_port_config)(struct audio_hw_device *dev,
                         const struct audio_port_config *config);
​
};
typedef struct audio_hw_device audio_hw_device_t;

在audio中,映射hw_device_t结构体的为audio_hw_device,其中第一个变量为hw_deivce_t类型的common,其余的都是指针函数,都是用于操作该audio_device,包括初始化检查、设置音量、设置静音状态、设置参数配置、打开/关闭output/input stream等等一些操作;

上述提及到的函数都为指针函数,在后续的audio_hw.c定义中都会做一一映射的操作;

audio_hw_device 赋值

在描述hw_module_methods_t的时候,我们知道,open函数指向了audio_hw.c中的adev_open函数,而在调用audio_hw_device_open函数时调用了open函数,即调用了adev_open函数;

而在adev_open函数中对audio_hw_device进行了初始化:

ini 复制代码
static int adev_open(const hw_module_t* module, const char* name,
                     hw_device_t** device)
{
    ALOGV("adev_open: %s", name);
​
    struct stub_audio_device *adev;
​
    if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
        return -EINVAL;
​
    adev = calloc(1, sizeof(struct stub_audio_device));
    if (!adev)
        return -ENOMEM;
​
    adev->device.common.tag = HARDWARE_DEVICE_TAG;
    adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
    adev->device.common.module = (struct hw_module_t *) module;
    adev->device.common.close = adev_close;
​
    adev->device.init_check = adev_init_check;
    adev->device.set_voice_volume = adev_set_voice_volume;
    adev->device.set_master_volume = adev_set_master_volume;
    adev->device.get_master_volume = adev_get_master_volume;
    adev->device.set_master_mute = adev_set_master_mute;
    adev->device.get_master_mute = adev_get_master_mute;
    adev->device.set_mode = adev_set_mode;
    adev->device.set_mic_mute = adev_set_mic_mute;
    adev->device.get_mic_mute = adev_get_mic_mute;
    adev->device.set_parameters = adev_set_parameters;
    adev->device.get_parameters = adev_get_parameters;
    adev->device.get_input_buffer_size = adev_get_input_buffer_size;
    adev->device.open_output_stream = adev_open_output_stream;
    adev->device.close_output_stream = adev_close_output_stream;
    adev->device.open_input_stream = adev_open_input_stream;
    adev->device.close_input_stream = adev_close_input_stream;
    adev->device.dump = adev_dump;
​
    *device = &adev->device.common;
​
    return 0;
}

在该初始化过程中涉及到的函数都是在audio_hw.c中定义的;

总结

至此,涉及到的3个结构体定义就清晰了;

相关推荐
快乐非自愿1 小时前
分布式系统架构2:服务发现
架构·服务发现
2401_854391081 小时前
SSM 架构中 JAVA 网络直播带货查询系统设计与 JSP 有效实现方法
java·开发语言·架构
264玫瑰资源库1 小时前
从零开始C++棋牌游戏开发之第二篇:初识 C++ 游戏开发的基本架构
开发语言·c++·架构
神一样的老师1 小时前
面向高精度网络的时间同步安全管理架构
网络·安全·架构
2401_857026231 小时前
基于 SSM 架构的 JAVA 网络直播带货查询系统设计与 JSP 实践成果
java·开发语言·架构
9527华安1 小时前
FPGA实现MIPI转FPD-Link车载同轴视频传输方案,基于IMX327+FPD953架构,提供工程源码和技术支持
fpga开发·架构·mipi·imx327·fpd-link·fpd953
老猿讲编程3 小时前
技术发展历程:从 CORBA 到微服务
微服务·云原生·架构
碳学长4 小时前
2025系统架构师(一考就过):案例题之一:嵌入式架构、大数据架构、ISA
大数据·架构·系统架构
brzhang7 小时前
十年磨一剑:那些关于长期软件开发的思考,架构设计中如何做好技术选型
前端·后端·架构
火龙kess8 小时前
使用FreeNAS软件部署ISCSI的SAN架构存储(IP-SAN)练习题
linux·运维·服务器·网络·windows·tcp/ip·架构