目录
- 说明
- 1. Android系统内为何会要有HAL ?
- 2. HAL_legacy和HAL对比
- 3. HAL module的架构分析
- 3.1 hw_module_t
- 3.2 hw_module_methods_t
- 3.3 hw_device_t
- 4. 分析hardware.c
- 5. 分析HAL的加载过程
- 6. 分析硬件访问服务
- 7. 官方实例mokoid分析
说明
- 在Android系统中有一个很特殊的HAL层,它的作用是什么呢?
- HAL是Android底层开发绕不开的Android独有机制,是操作系统上层和硬件驱动沟通的桥梁,通过这一系列的文章,你将对HAL有深入的理解。
1. Android系统内为何会要有HAL ?
想必在了解Android系统之前,都会听说Android系统是一个开放的操作系统,那么不禁要问,这“开放”和Linux的“开源”的区别是啥?
HAL层又叫硬件抽象层。它在Android体系中有着重要的意义,因为Android究竟是完全开源的还是完全不开源的秘密就在这一层。Google 将硬件厂商的驱动程序放在这一层。正是因为这层的代码没有开源,所以Android才被Linux家族删除。
在Android系统中,HAL层是为保护一些硬件提供商的知识产权而提出的,位于Linux内核层之上。HAL实现了硬件抽象化,将硬件平台的差异隐藏,为上层提供统一的硬件平台,从而加快开发人员在不同的硬件平台上进行代码移植。
就驱动开发的角度而言,HAL将Android框架与Linux内核隔离。主要原因有两方面:由于Linux内核要遵循GPL,根据此许可证,如果对Linux内核源码进行改动就必须公开其源码。如果Android系统的硬件驱动都在Linux的驱动模块中实现,就要将驱动源码完全公开。但是硬件设备商并不愿意将具体的细节公开,此类做法有损其利益。因此,Android系统源码采用Apache License2许可,它允许各厂家对Android系统源码进行改动但不公开。另一方面是因为Android对某些硬件有特殊的要求并没有标准的 Linux接口。
Android的HAL层考虑Linux系统的设计要求。由于Linux对硬件的支持实现不能完全在用户空间,只有内核空间才具有特权对硬件设备进行操作。基于上述因素,Android驱动将对硬件的支持放在内核空间与用户空间,内核空间使用Linux驱动模块,但只提供硬件访问通道,而用户空间则以HAL模块的方式,在HAL层对硬件细节与参数进行封装,既实现Linux系统对代码公开的要求,也保护移动设备厂家各自的利益。
在Android系统中可以分为如下 6 种HAL:
- 上层软件。
- 内部以太网。
- 内部通信CLIENT。
- 用户接入口。
- 虚拟驱动,设置管理模块
- 内部通信SERVER。
在Android系统中,定义硬件抽象层接口的代码具有以下5个特点:
- 硬件抽象层具有与硬件密切的相关性。
- 硬件抽象层具有与操作系统无关性。
- 接口定义的功能应包含硬件或系统所需硬件支持的所有功能。
- 接口定义简单明了,太多接口函数会增加软件模拟的复杂性。
- 具有可测性的接口设计有利于系统的软硬件测试和集成。
在Android 源码中,HAL 主要被保存在源码hardware目录中:
- libhardware_legacy:过去的目录,采取了链接库模块观念来架构。
- libhardware:新版的目录,被调整为用HAL stub观念来架构。
- ril:是Radio接口层。
- 其余目录为一些厂商定制的硬件HAL。
2. HAL_legacy和HAL对比
从Android源码上分析,HAL层主要对应的源码目录如下:
- libhardware_legacy:过去的目录,采取了链接库模块观念来架构。
- libhardware:新版的目录,被调整为用HAL stub观念来架构。
libhardware_legacy 架构中HAL层采用的是共享库的方式,也就是直接函数调用的方式使用HAL层的module,这种调用方式没有经过封装,上层直接操作底层的硬件设备,在编译时会调用到。但其明显的缺点是接口不统一,如果出现多个进程同时调用,由于需要对各进程空间进行映射会造成存储空间的浪费,同时存在代码的安全输人问题,容易造成系统崩溃。
libhardware架构中HAL层使用的是HAL module和HAL stub结合形式,Stub以动态链接库的方式存在。就操作而言,HAL层实现对库的隐藏。Android系统的HAL层包含很多的Stub,采用统一的调用方式对硬件进行操作。上层在HAL层调用Stub的函数,然后再回调这些操作函数。通过这样的模式,实现接口统一,而且各Stub没有关系,上层若要对某个硬件设备进行操作只要提供模块ID,就能对相关设备进行操作。HAL stub不是一个共享库,在编译时上层只拥有访问 HAL stub 的函数指针,并不需要 HAL stub。在上层通过HAL module 提供的统一接口获取并操作 HAL stub,所以文件只会被映射到一个进程而不会存在重复映射和重入问题。
从现在HAL层的结构可以看出,当前的HAL stub模式是一种代理人 (proxy)的概念,虽然stub仍以*.so文件的形式存在,但是HAL已经将*.so文件隐藏了。stub向HAL提供了功能强大的操作函数(Operations),而Runtime则从HAL获取特定模块(stub)的函数,然后再回调这些操作函数。这种以Indirect Function Call模式的架构,让HAL stub变成了一种“包含”关系也就是说在HAL里面包含了许多 stub(代理人)。Runtime只要说明module ID(类型)就可以取得操作函数。在当前的HAL模式中,Android 定义了HAL层结构框架,这样通过接口访问硬件时就形成了统一的调用方式。下图直观展示旧架构和新架构的不同之处:
HAL stub不是一个共享库,在编译时上层只拥有访问 HAL stub 的函数指针,并不需要 HAL stub。在上层通过HAL module 提供的统一接口获取并操作 HAL stub,所以文件只会被映射到一个进程而不会存在重复映射和重入问题。
3. HAL module的架构分析
Android 7的HAL采用HAL module和HAL stub结合的形式进行架构,HAL stub不是一个ShareLibrary(共享程序),在编译时上层只拥有访问HAL stub的函数指针并不需要HAL stub。上层通过HAL module提供的统一接口获取并操作HAL stub,so文件只会被mapping到一个进程,也不存在重复mapping和重入问题。
HAL module架构主要分为以下3个结构体:
- struct hw_module_t
- struct hw_module_methods_t
- struct hw_device_t
以上3个抽象概念在文件 hardware/libhardware/include/hardware/hardware.h 中进行了具体描述。对于不同的hardware的HAL,对应的lib命名规则是d.variant.so,例如:gralloc.msm7k.so 表示其id是gralloc,msm7k是variant。variant的取值范围是在该文件中定义的variant keys对应的值。
3.1 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;
/**
* The API version of the implemented module. The module owner is
* responsible for updating the version when a module interface has
* changed.
*
* The derived modules such as gralloc and audio own and manage this field.
* The module user must interpret the version field to decide whether or
* not to inter-operate with the supplied module implementation.
* For example, SurfaceFlinger is responsible for making sure that
* it knows how to manage different versions of the gralloc-module API,
* and AudioFlinger must know how to do the same for audio-module API.
*
* The module API version should include a major and a minor component.
* For example, version 1.0 could be represented as 0x0100. This format
* implies that versions 0x0100-0x01ff are all API-compatible.
*
* In the future, libhardware will expose a hw_get_module_version()
* (or equivalent) function that will take minimum/maximum supported
* versions as arguments and would be able to reject modules with
* versions outside of the supplied range.
*/
uint16_t module_api_version;
#define version_major module_api_version
/**
* version_major/version_minor defines are supplied here for temporary
* source code compatibility. They will be removed in the next version.
* ALL clients must convert to the new version format.
*/
/**
* The API version of the HAL module interface. This is meant to
* version the hw_module_t, hw_module_methods_t, and hw_device_t
* structures and definitions.
*
* The HAL interface owns this field. Module users/implementations
* must NOT rely on this value for version information.
*
* Presently, 0 is the only valid value.
*/
uint16_t hal_api_version;
#define version_minor hal_api_version
/** 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;
/** 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;
在结构体 hw_module_t中,需要注意如下5点:
- 在结构体hw_module_t 的定义前面有一段注释,意思是,硬件抽象层中的每一个模块都必须自定义一个硬件抽象层模块结构体,而且它的第一个成员变量的类型必须为 hw_module_t 。
- 硬件抽象层中的每一个模块都必须存在一个导出符号HAL_MODULE_IFNO_SYM,即HMI,它指向一个自定义的硬件抽象层模块结构体。后面在分析硬件抽象层模块的加载过程时,将会看到这个导出符号的意义。
- 结构体 hw_module_t 的成员变量 tag 的值必须设置为 HARDWARE_MODULE_TAG,即设置为一个常量值(‘H’<<24 | ‘W’<<16 | ‘M’<<8 | ‘T’),用来标志这是一个硬件抽层模块结构体。
- 结构体 hw_module_t 的成员量 dso 用来保存加硬抽象层模块后得到的句柄值。前面提到,每一个硬件抽象层模块都对应有一个动态链接库文件。加载硬件抽象层模块的过程实际上就是调用 dlopen() 函数来加载与其对应的动态链接库文件的过程。在调用 dlclose() 函数来卸载这个硬件抽象层模块时,要用到这个句柄值,因此,在加载时需要将其保存起来。
- 结构体 hw_module_t的成员变量 methods 定义了一个硬件抽象层模块的操作方法列表,其类型为hw_module_methods_t 。
3.2 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_module_methods_t 中只有一个成员,它是一个函数指针,用来打开硬件抽象层模块中的硬件设备。由于一个硬件抽象层模块可能会包含多个硬件设备,因此在调用结构体 hw_module_methods_t 的成员量open,打开一个硬件设备时需要指定其ID。其中:
- 参数 module :表示要打开的硬件设备所在的模块。
- 参数 id :表示要打开的硬件设备的ID。
- 参数 device :是一个输出参数,用来描述一个已经打开的硬件设备。
3.3 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;
/**
* Version of the module-specific device API. This value is used by
* the derived-module user to manage different device implementations.
*
* The module user is responsible for checking the module_api_version
* and device version fields to ensure that the user is capable of
* communicating with the specific module implementation.
*
* One module can support multiple devices with different versions. This
* can be useful when a device interface changes in an incompatible way
* but it is still necessary to support older implementations at the same
* time. One such example is the Camera 2.0 API.
*
* This field is interpreted by the module user and is ignored by the
* HAL interface itself.
*/
uint32_t version;
/** 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;
在结构体hw_device_t中,需要注意如下3点:
- 硬件抽象层模块中的每一个硬件设备都必须自定义一个硬件设备结构体,而且它的第一个成员变量的类型必须为 hw_device_t 。
- 结构体 hw_device_t 的成员变量 tag 的值必须设置为HARDWARE_DEVICE_TAG,即设置为个常量值(‘H’<<24 | ‘W’<<16 | ‘M’<<8 | ‘T’),用来标志这是一个硬件抽象层中的硬件设备结构体。
- 结构体 hw_device_t 的成员变量 close 是一个函数指针,用来关闭一个硬件设备。