Android 硬件抽象层HAL学习

HAL 全称 Hardware Abstract Layer,即硬件抽象层,它向下屏蔽了硬件的实现细节,向上提供了抽象接口,HAL是底层硬件和上层框架直接的接口,框架层通过HAL可以操作硬件设备。

Android 系统架构

安卓的系统架构如下图所示,从上至下分为五个部分:

详细见Android 架构学习

HAL

传统的HAL在Android 8.0已经弃用

为什么需要HAL?

(1)HAL层屏蔽掉不同硬件设备的差异,为Android提供了统一的设备访问接口。不同的硬件厂商遵循HAL标准来实现自己的硬件控制逻辑,开发者不必关心硬件设备的差异,只需按照HAL提供的标准接口对硬件进行访问即可。

(2)HAL层帮助硬件厂商隐藏了设备的核心细节,HAL层位于用户空间,遵循Apache协议,允许硬件厂商不公开源码,将设备相关的实现放在Android系统中HAL具有两种实现方式:Legacy以及Stub HAL,初期使用的是Legacy HAL的方式,该方式为标准的Linux共享库,其它应用程序直接调用HAL层共享库导出的函数。Google后来提出了Stub HAL的方式,仍然以共享库(.so)的形式提供,它把所有供外部访问的的方法(函数)的入口指针保存在统一的数据结构,其它程序需要访问HAL中方法时,需要先获得Stub,然后通过具体的函数指针去读写底层设备。

HAL开发规范

Android HAL的软件架构比较简单,其中有两个重要的数据结构:hw_module_t, hw_device_t

Android HAL软件设计中有两个概念:Module,Device。Module代表整个HAL实现,是HAL模块类别、功能的封装,是外部程序能看到的唯一视角;Deivce代表着一个实际的硬件设备,是设备属性、设备操作的封装,设备提供的所有操作在Device结构中体现,Device由Module的open方法创建。一旦一个应用程序通过通过HAL的API获得了某个具体的HAL Module,便可通过open方法获得一个Device,Module和Device即是HAL的Stub,有了Stub就可以操作HAL模块和设备。

定义代表Module的数据结构

所有HAL模块代码均具有类似的数据结构,以Power HAL模块为例,首先是定义一个表示Power模块的数据结构power_module_t,该数据结构须遵循以下准测:

  1. 第一个成员必须是struct hw_module_t;
  2. 创建一个该数据结构的变量,并以HAL_MODULE_INFO_SYM命名;
  3. 结构体hw_module_ttag成员固定赋值为HARDWARE_MODULE_TAG;
  4. 结构体hw_module_tmodule_api_version代表该HAL模块的API版本,开发人员须在模块接口改变时更新该成员;
  5. 结构体hw_module_thal_api_version代表HAL接口的API版本(即harware.h hardware.c中定义的数据结构和接口函数的版本),该成员目前只能赋值为HARDWARE_HAL_API_VERSION;
  6. 结构体hw_module_tid成员是该模块的标识符,其他程序通过该成员来寻找对应的HAL的Stub。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <hardware/hardware.h>

typedef struct power_module {
struct hw_module_t common;
...
...
} power_module_t;

struct power_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = POWER_MODULE_API_VERSION_0_2,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = POWER_HARDWARE_MODULE_ID,
.name = "Default Power HAL",
.author = "The Android Open Source Project",
.methods = &power_module_methods, /* hw_module_methods_t */
},
...
};

hw_module_methods_t结构体定义各异打开具体设备的open函数

1
2
3
4
5
6
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;

定义代表Device的数据结构

模块代表一个实际存在的HAL,通过模块你并不能实际操作设备。开发者需要将设备相关的属性以及操作封装在代表设备的数据结构中。例如你可以创建一个指纹设备fingerprint_device_t,并将注册指纹(enroll)、删除指纹(remove)等操作封装在fingerprint_device_t中。代表Device的数据结构体现了设备的属性以及改设备所提供的操作接口,在这个数据结构中通常是一系列的函数指针。如下是一个简单的例子:

1
2
3
4
5
6
7
typedef struct fingerprint_device {
struct hw_device_t common;
int (*enroll)(struct fingerprint_device *dev, const hw_auth_token_t *hat, uint32_t gid, uint32_t timeout_sec);
int (*remove)(struct fingerprint_device __unused *dev, uint32_t __unused gid, uint32_t __unused fid);
...
...
} fingerprint_device_t;

与xxx_module_t数据结构的规则类似,xxx_device_t的第一个成员必须是struct hw_device_t。hw_device_t的tag成员必须被赋值为HARDWARE_DEVICE_TAG。

实现open方法

其他应用程序使用HAL的步骤通常是:

  1. 通过hw_get_module获得指定HAL模块的hw_module_t的引用;
  2. 调用hw_module_t->methods->open方法获取对device的引用。
  3. 通过device中的Stub(即封装的函数指针)来操作设备。

open方法位于数据结构hw_module_methods_t中,open方法是这个数据结构的唯一成员,而close方法存在于hw_device_t数据结构中。 open方法用于从HAL中获取一个device的stub,从而得到这个设备的详细信息例如设备的属性以及设备提供的操作接口(函数指针)。因此open方法在不同HAL中的实现也较为相似,大致为:

  1. 为xxx_device_t分配内存。
  2. 对common成员(hw_device_t类型)赋值。
  3. 对函数指针赋值。
  4. 将创建的xxx_device通过形参 hw_device_t** device返回

以fingerprint 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static int fingerprint_open(const hw_module_t* module, const char __unused *id,
hw_device_t** device)
{
if (device == NULL) {
ALOGE("NULL device on open");
return -EINVAL;
}
fingerprint_device_t *dev = malloc(sizeof(fingerprint_device_t));
memset(dev, 0, sizeof(fingerprint_device_t));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = FINGERPRINT_MODULE_API_VERSION_2_0;
dev->common.module = (struct hw_module_t*) module;
dev->common.close = fingerprint_close;
dev->pre_enroll = fingerprint_pre_enroll;
dev->enroll = fingerprint_enroll;
dev->get_authenticator_id = fingerprint_get_auth_id;
dev->cancel = fingerprint_cancel;
dev->remove = fingerprint_remove;
dev->set_active_group = fingerprint_set_active_group;
dev->authenticate = fingerprint_authenticate;
dev->set_notify = set_notify_callback;
dev->notify = NULL;
*device = (hw_device_t*) dev;
return 0;
}

HAL类型

HAL 类型 | Android Open Source Project (google.cn)

hal开发之hidl/aidl支持的绑定式直通式详细讲解_android hal 绑定式 开发-CSDN博客

HAL与HIDL

https://source.android.com/docs/core/architecture/hidl?hl=zh-cn

HAL 接口定义语言(简称 HIDL)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是指用于在可以独立编译的代码库之间进行通信的系统。从 Android 10 开始,HIDL 已废弃,Android 将在所有位置改用 AIDL

HIDL 旨在用于进程间通信 (IPC)。使用 HIDL 创建的 HAL 称为绑定式 HAL,因为它们可以使用 Binder 进程间通信 (IPC) 调用与其他架构层进行通信。绑定式 HAL 在独立于使用它们的客户端的进程中运行。对于必须与进程相关联的代码库,还可以使用透传模式(在 Java 中不受支持)。

HIDL 改变了之前上层直接调用 HAL 层的模式。而是将 HAL 层作为一个服务启动,当上层需要调用时是作为 client 来请求服务的。主要的区别就是由一个进程变为两个进程,因此进程间的通信(IPC)底层使用的是 Binder,好处是上层和 HAL 层解耦,并且多进程调用同一个接口时,使用过去的方式会被映射到每个进程中,造成浪费,而使用现在的方式只会在一个进程空间中。

HIDL开发流程:https://evilpan.com/2020/11/01/hidl-basics/


Android 硬件抽象层HAL学习
https://chujian521.github.io/blog/2024/05/19/Android-硬件抽象层HAL学习/
作者
Encounter
发布于
2024年5月19日
许可协议