众所周知,现代的计算机系统由硬件系统以及软件系统两个部分组成,再好的硬件,如果没有软件的支持,就不能发挥其应有的作用。没有硬件,软件也就没有了运行条件的支持。手机中的软硬件关系也是如此。
Android系统从上而下分为以下几层:应用层(Application)
->框架层(Framework)
->外部库及Android运行时(External Libraries & Android Runtime)
->硬件抽象层(HAL)
->Linux内核(Linux Kernel)
这几层。其中,直接与硬件相关的是内核
和硬件抽象层
。由于android的Linux内核只提供简单的访问硬件逻辑,而从硬件中读取了什么值或者写入了什么值到硬件的逻辑则放入了硬件抽象层中。而这部分由于出于保护厂家的利益,遵循Apache License,不必进行开源,正是由于这样,Android从Linux内核主线的代码树中被踢出去了。所以说Android是一个开放的系统而不是开源的系统。
HAL
从上而下涉及到android系统的硬件驱动层、硬件抽象层、运行时库以及应用程序框架层等,因此,学习HAL层
,对理解Android整个系统具有重要的意义。而这一层的代码遵循的一定的规范,了解这些规范,有利于阅读Android源码时的理解。
Android系统的硬件抽象层采用模块的形式来管理硬件的访问接口。每个硬件抽象模块都对应一个动态链接库。这些动态链接库的命名要满足一定的规范。同时,Android使用hw_module_t
结构体和hw_device_t
分别描述每个硬件抽象模块和硬件抽象模块中的每个硬件设备。下面就来看看硬件抽象模块的命名规范以及hw_module_t
和hw_device_t
的定义吧。
Android的硬件抽象模块的命名规范在hardware/libhardware/hardware.c
文件中定义,下面就来解析一下它的意思。
/**
* There are a set of variant filename for modules. The form of the filename
* is "<MODULE_ID>.variant.so" so for the led module the Dream variants
* of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
*
* led.trout.so
* led.msm7k.so
* led.ARMV6.so
* led.default.so
*/
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"
};
由上面代码可以知道,硬件抽象层的命名规则为<MODULE_ID>.variant.so
,其中MODULE_ID
表示硬件抽象模块的唯一编号ID,而variant
则为ro.hardware
、ro.product.board
、ro.board.platform
以及ro.arch
四个系统属性中的一个类型,系统启动后按照定义的属性的顺序逐一取相应的值,若值存在,则使用该值作为variant
的值,否则就使用xxx.default.so
作为默认的抽象模块文件来加载硬件抽象层。
在hardware/libhardware/include/hardware.h
文件中,定义了ht_module_t
、hw_device_t
及相关结构体。下面来了解一下吧(为便于描述,代码有些许调整)
****struct hw_module_t****
/*
* Value for the hw_module_t.tag field
*/
#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')
/**
* Name of the hal_module_info
*/
#define HAL_MODULE_INFO_SYM HMI
/**
* Name of the hal_module_info as a string
*/
#define HAL_MODULE_INFO_SYM_AS_STR "HMI"
/**
* 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;
从上面代码可以看到:
(1)每个硬件模块都必须定义一个硬件抽象模块结构体,而且其第一个成员变量的类型必须是hw_module_t
类型的变量
(2)硬件抽象层的每一个模块都必须存在一个导出符号HAL_MODULE_INFO_SYM
,即HMI
,它指向一个自定义的硬件抽象层模块结构体,换句话说,就是要存在一个变量名为HMI的自定义的硬件抽象层模块的结构体变量。到后续博文讲述硬件抽象层模块的加载过程会看到具体的使用。
(3)结构体struct hw_module_t
的tag
变量必须设为HARDWARE_MODULE_TAG
即(((H)<<24)|((W)<<16)|((M)<<8)|(T))
常量。
(4)hw_module_t
结构体的dso
变量用来保存加载硬件抽象模块后得到的句柄值,每一个硬件抽象模块都对应一个动态链接库文件,硬件抽象模块的加载过程其实就是使用dlopen
函数打开对应的动态链接库文件获得这个句柄的过程,而使用dlclose
函数进行硬件抽象模块的卸载时需要用到这个句柄,因此需要把它保存起来,供之后卸载使用。
(5)hw_module_t
结构体的methods
给出了一个硬件抽象模块的操作方法列表,它的类型为struct hw_module_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
的函数指针变量,用来打开硬件抽象层模块中的硬件设备。其中,module
表示硬件设备所处的硬件抽象模块,由于一个硬件抽象模块可能包含若干个设备,因此我们需要提供唯一标示设备的id
。前面说过,android使用hw_device_t
来表示硬件抽象模块中的硬件设备,所以用参数device
来表示一个已经打开的设备。下面来看看hw_device_t
结构体的定义。
/**
* Every device data structure must begin with hw_device_t
* followed by module specific public methods and attributes.
* 使用hw_device_t结构体来描述模块代表的硬件设备
*/
typedef struct hw_device_t {
/** tag must be initialized to HARDWARE_DEVICE_TAG */
uint32_t tag; //设备标记,必须使用系统定义的HARDWARE_DEVICE_TAG来初始化
uint32_t version; //版本
/** reference to the module this device belongs to */
struct hw_module_t* module; //hw_module_t信息,硬件抽象模块
/** 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;
从上面可以看到:
(1)和hw_module_t
的说明一样,硬件抽象模块中的每一个硬件设备都必须自定义一个硬件设备结构体,该结构的第一个变量的类型必须为hw_device_t
(2)hw_device_t
的tag
变量必须用HARDWARE_DEVICE_TAG
即常量(((H)<<24)|((W)<<16)|((M)<<8)|(T))
,用来标识这是一个硬件设备结构体
(3)函数指针close
用来关闭一个硬件设备。
从上面的代码中,我们来总结一下在android硬件抽象层的编码规范:
(1)所写的抽象模块动态库的命名必须以<MODULE_ID>.variant.so
为格式命名
(2)需要自定义硬件抽象模块结构体,其中第一个变量的类型必须为struct hw_module_t
,同时,该自定义硬件抽象模块结构体的变量名必须为HAL_MODULE_INFO_SYM
即HMI
。同时提供相应的操作列表hw_method_t
(3)需要自定义硬件抽象模块的硬件设备结构体,其中第一个变量的类型必须为struct hw_device_t
。
本篇博文就写到这里,第一次认真写,还真是不太容易啊,希望自己要努力坚持!!后面将继续分析硬件抽象模块的加载过程以及如何实现从内核驱动到应用层代码的编写,当然,水平有限,要借助罗升阳老师博客【博客地址】的力量~~