HAL so库加载机制---之一

来源:互联网 发布:javascript中文手册 编辑:程序博客网 时间:2024/05/29 08:28

1,概述

Android HAL(Hardware Abstract Layer)硬件抽象层,从字面意思可以看出是对硬件设备的抽象和封装,

为Android在不同硬件设备提供统一的访问接口。HAL处于Android framework和Linux kernel driver之间,

HAL存在的意义有以下2个方面:

1,HAL屏蔽了不同硬件设备的差异,为Android提供了统一的访问硬件设备的接口。

     不同的硬件厂商遵循HAL标准来实现自己的硬件控制逻辑,但开发者不必关心不同硬件设备的差异,

     只需要按照HAL提供的标准接口访问硬件就可以了。

2,HAL层帮助硬件厂商隐藏了设备相关模块的核心细节。硬件厂商处于利益考虑,不希望公开硬件设备相关的实现细节;

     有了HAL层之后,他们可以把一 些核心的算法之类的东西的实现放在HAL层,而HAL层位于用户空间,不属于linux内核,

     和android源码一样遵循的是Apache license协议,这个是可以开源或者不开的。

搞清楚了HAL存在的作用,就可以对其框架做个简单的总结。这里从以下3个方面来简单分析下HAL架构.

1,分析HAL的2个核心数据结构:hw_module_t 和 hw_device_t;

2,描述HAL是如何查询和加载设备动态共享库的;

3,以GPS为例,简单分析上层是如何使用HAL来访问硬件设备的。

本文基于android 6.0。

2, 数据结构

hw_module_t结构体:Android HAL将各类硬件设备抽象为硬件模块,HAL使用该结构体描述一类硬件抽象模块。

每个硬件抽象模块都对应一个动态链接库,一般是由厂商提供的,这个动态链接库必须尊重HAL的命名规范才能被HAL加载到,后面会看到。

hw_device_t结构体:每一类硬件抽象模块又包含多个独立的硬件设备,HAL使用该结构体描述硬件模块中的独立硬件设备。

2.1 hw_module_t

hardware.h文件中的hw_module_t结构体定义如下,

typedef struct hw_module_t {uint32_t tag;uint16_t module_api_version;#define version_major module_api_versionuint16_t hal_api_version;#define version_minor hal_api_versionconst char *id;const char *name;const char *author;struct hw_module_methods_t* methods;void* dso;#ifdef __LP64__    uint64_t reserved[32-7];#elseuint32_t reserved[32-7];#endif} hw_module_t;

注意:

1, 每个硬件模块必须包含一个名字为HAL_MODULE_INFO_SYM的结构体;

这个结构体的第一个元素必须为hw_module_t,然后后面可以增加模块相关的其他信息。

这里可以理解为是一种继承关系,相当于应硬件模块的HAL_MODULE_INFO_SYM结构体,继承了hw_module_t,

只不过是C语言中没有继承的概念,是通过在结构体中包含的方式间接实现的。

HAL_MODULE_INFO_SYM值定义同样在hardware.h中:

#define HAL_MODULE_INFO_SYM         HMI#define HAL_MODULE_INFO_SYM_AS_STR  "HMI"

2, tag必须被初始化HARDWARE_MODULE_TAG常量,其定义如下,

#define MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D))#define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T')#define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T')

3,dso:用来保存加载硬件抽象模块后得到的句柄值,前面提到每一个硬件抽象模块都对应一个动态链接库,

硬件抽象模块的加载过程实际是使用dlopen函数打开对应的动态链接库文件获得这个句柄;

使用dlclose函数进行硬件抽象模块的卸载是需要用到这个句柄,因此需要保存起来。

4, hw_module_methods_t结构体方法定义如下,

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;

该结构体只有一个函数指针open,打开硬件模块获取模块中的硬件设备。由于一个硬件抽象模块中可能包含多个设备,

因此需要根据传入的设备id来获取相应的硬件设备he_device_t。所以这里的device就表示一个已经打开的硬件设备。

2.2 hw_device_t

hardware.h文件中的hw_device_t结构体定义如下,

typedef struct hw_device_t {    uint32_t tag;    uint32_t version;struct hw_module_t* module;#ifdef __LP64__    uint64_t reserved[12];#else    uint32_t reserved[12];#endif    int (*close)(struct hw_device_t* device);} hw_device_t;

module:表示该硬件设备归属于哪一个硬件抽象模块。

close:函数指针,用来关闭硬件设备。

小结:到此,HAL的2个核心数据结构体就分析完了;硬件厂商必须遵循HAL规范和命名,实现hw_module_t结构体;

并且提供open函数来获取hw_device_t。下面看看HAL到底是怎样获取硬件模块和硬件设备的,是如何加载和解析对应的动态共享库的。

3, HAL库规范

HAL会按照variant_keys[]定义的属性名称的顺序逐一来读取属性值,若值存在,

则作为variant的值加载对应的动态链接库。如果没有读取到任何属性值,

则使用<MODULE_ID>.default.so 作为默认的动态链接库文件名来加载硬件模块。

hardware.c文件中的variant_keys定义如下,

static const char *variant_keys[] = {    "ro.hardware",  /* This goes first so that it can pick up a different                       file on the emulator. */    "ro.product.board",    "ro.board.platform",    "ro.arch"};

variant_keys中模块名个数,逐个查找。

static const int HAL_VARIANT_KEYS_COUNT =    (sizeof(variant_keys)/sizeof(variant_keys[0]));

有了模块的文件名字规范,那么共享库的存放路径也是有规范的。HAL规定了2个硬件模块动态共享库的存放路径,

#if defined(__LP64__)#define HAL_LIBRARY_PATH1 "/system/lib64/hw"#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"#else#define HAL_LIBRARY_PATH1 "/system/lib/hw"#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"#endif

也就是说硬件模块的共享库必须放在/system/lib/hw 或者 /vendor/lib/hw 这2个路径下的其中一个。

HAL在加载所需的共享库的时候,会先检查HAL_LIBRARY_PATH2路径下面是否存在目标库;

如果没有,继续检查HAL_LIBRARY_PATH1路径下面是否存在。具体实现在hw_module_exists方法中,如下,

static int hw_module_exists(char *path, size_t path_len, const char *name, const char *subname){    snprintf(path, path_len, "%s/%s.%s.so", HAL_LIBRARY_PATH2, name, subname);    if (access(path, R_OK) == 0)        return 0;    snprintf(path, path_len, "%s/%s.%s.so",HAL_LIBRARY_PATH1, name, subname);    if (access(path, R_OK) == 0)        return 0;    return -ENOENT;}

name:其实对应上面提到的MODULE_ID

subname: 对应从上面提到的属性值variant

原创粉丝点击