文章目录
什么是HAL
硬件抽象层:Hardware abstraction layer,缩略语为:HAL。是介于android Framework和 linux kernel之间的抽象出来的一层结构。 对上层调用提供统一的接口,上层应用不需要知道下层硬件是如何具体实现的,屏蔽了底层实现的细节。
定义HAL层的原因
- Android 使用的开源协议是Apache Liscence,这个协议比较宽松,允许开发者获得并且修改了源代码之后,不公布源代码; 而linux 使用的是开源协议GPL,它的限制比较多,它要求开发者添加或者修改了源码之后,必须把添加或者修改后的源码公布出来。
- 如果 Android 系统像其他 Linux 系统一样,把对硬件的支持完全实现在 Linux 内核的驱动模块中,那么硬件厂商就必须将这些硬件驱动源码公开,这样就可能损害到厂商的利益,因为这相当于暴露了硬件的实现细节和参数。
- 所以,Android 就在用户空间搞了一个 HAL 层,将硬件的一些重要的操作都放在这一层中完成,这些操作都封装在厂商所提供的一个动态链接库中,从而达到了避免源码公开的目的,而底层 Linux 内核空间中的设备驱动模块,现在则只提供一些最基本的硬件设备寄存器操作的功能。
HAL层的基本知识
- HAL 实现通常会以.so的形式,内置在共享库模块中,但 Android 并不要求 HAL 实现与设备驱动程序之间进行标准交互,因此可以视情况采取适当的做法。不过,要使 Android 系统能够与您的硬件正确互动,您必须遵守各个特定于硬件的 HAL 接口中定义的规则。
- 为了保证 HAL 具有可预测的结构,每个特定于硬件的 HAL 接口都要具有 hardware/libhardware/include/hardware/hardware.h 中定义的属性。这类接口可让 Android 系统以一致的方式加载 HAL 模块的正确版本。
HAL层框架分析
结构体
要实现一个Android 的HAL,需要实现下面这三个结构体:
hw_module_t : 用来描述硬件模块
hw_device_t : 用来描述硬件设备
hw_module_methods_t: 用来打开硬件模块中包含硬件设备,获得指向硬件设备结构体的指针
在下面的文件中描述了HAL的编写规范,并且给出了标准接口
代码路径:/hardware/libhardware/include/hardware/hardware.h
hw_module_t
/**
* 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 {
/** tag must be initialized to HARDWARE_MODULE_TAG */
uint32_t tag; //tag,根据引文注释可以看到必须被初始化为HARDWARE_MODULE_TAG
/** major version number for the module */
uint16_t version_major;//主版本号
/** minor version number of the module */
uint16_t version_minor;//次版本号
/** Identifier of module */
const char *id;//模块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;//硬件模块方法结构体
/** module's dso */
void* dso;//打开硬件模块的库时得到的句柄
/** padding to 128 bytes, reserved for future use */
uint32_t reserved[32-7];
} hw_module_t;
1. 每个硬件模块中都要定义一个名字叫做 HAL_MODULE_INFO_SYM 结构体变量,而这结构体变量中的第一个成员必须是 hw_module_t
类型。也就是说,每个硬件模块都要自己实现一个结构体,但是这个结构体的第一个成员必须是 hw_module_t 结构体类型。
2. 这里有一种继承思想,hw_module_t 是一个基类,描述所有硬件模块都应该具有的一些属性,具体到某个具体的硬件模块实现时,都需要
继承hw_module_t 结构,也就是说hw_module_t 是所有特定硬件模块的父类。
3. hw_module_t 中包含一个hw_module_methods_t 的结构体,hw_module_methods_t 这个结构体定义了一个重要的open函数指针,需要硬件
厂商去实现
hw_module_methods_t
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;
hw_device_t
/**
* 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; //设备tag
/** version number for hw_device_t */
uint32_t version;//版本
/** reference to the module this device belongs to */
struct hw_module_t* module;//本设备归属的硬件模块
/** padding reserved for future use */
uint32_t reserved[12];//保留
/** Close this device */
int (*close)(struct hw_device_t* device);//关闭设备的函数指针
} hw_device_t;
1. hw_device_t是在hw_module_t 中open 函数返回的一个结构,它用来描述所有硬件设备的属性,所有硬件设置相关的接口也会在这里实现,
2. 和hw_module_t 类似,hw_device_t 也可以看做是一个基类,描述了所有硬件设备应该具有的属性,然后具体到某个特定的硬件设备实现时,
都需要继承相应的hw_device_t 结构。所以,每个HAL层硬件设备对应的结构体中的第一个成员必须是hw_device_t。比如说,传感器模块,
sensor_module,是一个硬件模块,但是手机中的传感器就对应的有好多种,比如加速度acc_sensor,磁传感器M_sensor等,那么他们都属于
sensor_module,但是他们有都有自己的hw_device_t结构体来描述。
常量
#define HAL_MODULE_INFO_SYM HMI
#define HAL_MODULE_INFO_SYM_AS_STR "HMI"
函数
int hw_get_module(const char *id, const struct hw_module_t **module) // 通过ID获取模块相关联的信息
{
return hw_get_module_by_class(id, NULL, module);
}
当用户调用hw_get_module()函数时,将硬件的id名进行传入,那么函数将会从当前系统中,注册的硬件模块里查找对应的硬件模块,
并通过module二级指针进行返回。
HAL类型
HAL的调用逻辑
使用hw_get_module 传入目标HAL的id
根据传入的HAL id,拼装成路径,去寻找对应的.so文件
使用dlopen加载打开对应的so文件,传回一个操作句柄handle
调用 dlsym函数解析打开的库,即**用dlsym去寻找HAL_MODULE_INFO_SYM_AS_STR名字的结构体**
通过dlsym解析之后就得到了hw_module_t,将从库中解析得到的结构体中的id和传入的id做比较,看是否一致。如果一致则证明就是得到正确的硬件模块。
最后将得到的module结果赋值给传入的第三个参数hw_module_t
HAL module命名必须是HAL_MODULE_INFO_SYM的原因
每个HAL module 都使用HAL_MODULE_INFO_SYM作为xxx_module_t的结构体名称。
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 = "NVIDIA Tegra Audio HAL",
.author = "The Android Open Source Project",
.methods = &hal_module_methods,
},
};
通过dlsym去寻找HAL_MODULE_INFO_SYM_AS_STR这个统一的名称,
因为每个HAL module 结构体中的第一个成员必须是struct hw_module_t,且只有一个
所以在dlsym 找到 xxx_module_t 以后,返回的地址是xxx_module_t 地址,也是第一个成员变量的地址,也就是hw_module_t 模块
这样就能调用 module 的 open 函数,去找到xxx _device_t, hw_device_t必须要在第一个的原因也是如此
实现HAL设备
扩展知识
参考链接
https://www.jianshu.com/p/2dafe06cda15
https://www.cnblogs.com/qiangge-python/p/10142249.html
http://t.zoukankan.com/Cqlismy-p-11823592.html