系统的硬件抽象层以模块的形式来管理各个硬件访问接口。每一个硬件模块都对应有一个动态链接库文件,这些动态链接库文件的命令需要符合一定的规范。同时,在系统内部,每一个硬件抽象层模块都使用结构体hw_module_t来描述,而硬件设备则使用结构体hw_device_t来描述。接下来,我们就分别描述硬件抽象层模块文件的命名规范以及结构体hw_module_t和hw_device_t的定义。
- 2.3.1.1 硬件抽象层模块文件命名规范
-
-
-
hardware/libhardware/hardware.c
01 /**
02 * There are a set of variant filename for modules. The form of the filename
03 * is "<MODULE_ID>.variant.so" so for the led module the Dream variants
04 * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
05 *
06 * led.trout.so
07 * led.msm7k.so
08 * led.ARMV6.so
09 * led.default.so
10 */
11
12 static const char *variant_keys[] = {
13 "ro.hardware", /* This goes first so that it can pick up a different
14 file on the emulator. */
15 "ro.product.board",
16 "ro.board.platform",
17 "ro.arch"
18 };这段代码和注释的意思是,硬件抽象层模块文件的命名规范为<MODULE_ID>.variant.so”,其中,MODULE_ID表示模块的ID,variant表示四个系统属性ro.hardware、ro.product.board、ro.board.platform和ro.arch之一。
系统属性ro.hardware是在系统启动时,由init进程负责设置的。它首先会读取/proc/cmdline文件,检查里面有没有一个名称为androidboot.hardware的属性,如果有,就把它的值作为属性ro.hardware的值;否则,就将/proc/cpuinfo文件的内容读取出来,并且将里面的硬件信息解析出来,即将Hardware字段的内容作为属性ro.hardware的值。例如,在Android模拟器中,从/proc/cpuinfo文件读取出来的Hardware字段内容为goldfish,于是,init进程就会将属性ro.hardware的值设置为“goldfish”。系统属性ro.product.board、ro.board.platform和ro.arch是从/system/build.prop文件读取出来的。文件/system/build.prop是由编译系统中的编译脚本build/core/Makefile和Shell脚本build/tools/buildinfo.sh生成的,有兴趣的读者可以研究一下这两个文件,这里就不深入分析了。
-
系统在加载硬件抽象层模块时,依次按照ro.hardware、ro.product.board、ro.board.platform和ro.arch的顺序来取它们的属性值。如果其中的一个系统属性存在,那么就把它的值作为variant的值,然后再检查对应的文件是否存在,如果存在,那么就找到要加载的硬件抽象层模块文件了;否则,就继续查找下一个系统属性。如果这四个系统属性都不存在,或者对应于这四个系统属性的硬件抽象层模块文件都不存在,那么就用“<MODULE_ID>.default.so”来作为要加载的硬件抽象层模块文件的名称。
-
-
结构体hw_module_t和hw_device_t及其相关的其他结构体定义在文件hardware/libhardware/include/hardware/hardware.h中。下面我们就分别介绍这些结构体的定义。
hw_module_t
hardware/libhardware/include/hardware/hardware.h
01 /*
02 * Value for the hw_module_t.tag field
03 */
04
05 #define MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D))
06 #define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T')
07
08 /**
09 * Name of the hal_module_info
10 */
11 #define HAL_MODULE_INFO_SYM HMI
12
13 /**
14 * Every hardware module must have a data structure n amed HAL_MODULE_INFO_SYM
15 * and the fields of this data structure must begin with hw_module_t
16 * followed by module specific information.
17 */
18 typedef struct hw_module_t {
19 /** tag must be initialized to HARDWARE_MODULE_TAG */
20 uint32_t tag;
21
22 /** major version number for the module */
23 uint16_t version_major;
24
25 /** minor version number of the module */
26 uint 16_t version_minor;
27
28 /** Identifier of module */
29 const char *id;
30
31 /** Name of this module */
32 const char *name;
33
34 /** Author/owner/implementor of the module */
35 const char *author;
36
37 /** Modules methods */
38 struct hw_module_methods_t* methods;
39
40 /** module's dso */
41 void* dso;
42
43 /** padding to 128 bytes, reserved for future use * /
44 uint32_t reserved[32-7];
45
46 } hw_module_t;结构体hw_module_t中的每一个成员变量在代码中都有详细的解释,这里不再重复。不过,有五点是需要注意的。
(1)在结构体hw_module_t的定义前面有一段注释,意思是,硬件抽象层中的每一个模块都必须自定义一个硬件抽象层模块结构体,而且它的第一个成员变量的类型必须为hw_module_t。
(2)硬件抽象层中的每一个模块都必须存在一个导出符号HAL_MODULE_IFNO_SYM,即“HMI”,它指向一个自定义的硬件抽象层模块结构体。后面我们在分析硬件抽象层模块的加载过程时,将会看到这个导出符号的意义。
(3)结构体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定义了一个硬件抽象层模块的操作方法列表,它的类型为hw_module_methods_t,接下来我们就介绍它的定义。
hw_module_methods_t
hardware/libhardware/include/hardware/hardware.h
1 typedef struct hw_module_methods_t {
2 /** Open a specific device */
3 int (*open)(const struct hw_module_t* module, const char* id,
4 struct hw_device_t** device);
5
6 } hw_module_methods_t;结构体hw_module_methods_t只有一个成员变量,它是一个函数指针,用来打开硬件抽象层模块中的硬件设备。其中,参数module表示要打开的硬件设备所在的模块;参数id表示要打开的硬件设备的ID;参数device是一个输出参数,用来描述一个已经打开的硬件设备。 由于一个硬件抽象层模块可能会包含多个硬件设备,因此,在调用结构体hw_module_methods_t的成员变量open来打开一个硬件设备时,我们需要指定它的ID。硬件抽象层中的硬件设备使用结构体hw_device_t来描述,接下来我们就介绍它的定义。
hw_device_t
hardware/libhardware/include/hardware/hardware.h
01 #define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T')
02
03 /**
04 * Every device data structure must begin with hw_device_t
05 * followed by module specific public methods and attributes.
06 */
07 typedef struct hw_device_t {
08 /** tag must be initialized to HARDWARE_DEVICE_TAG */
09 uint32_t tag;
10
11 /** version number for hw_device_t */
12 uint32_t version;
13
14 /** reference to the module this device belongs to */
15 struct hw_module_t* module;
16
17 /** padding reserved for future use */
18 uint32_t reserved[12];
19
20 /** Close this device */
21 int (*close)(struct hw_device_t* device);
22
23 } hw_device_t;结构体hw_device_t中的每一个成员变量在代码中都有详细的解释,这里不再重复。不过,有三点是需要注意的。
(1)硬件抽象层模块中的每一个硬件设备都必须自定义一个硬件设备结构体,而且它的第一个成员变量的类型必须为hw_device_t。
(2)结构体hw_device_t的成员变量tag的值必须设置为HARDWARE_DEVICE_TAG,即设置为一个常量值('H' <<24 | 'W'<<16 | 'D'<<8 | 'T'),用来标志这是一个硬件抽象层中的硬件设备结构体。
(3)结构体hw_device_t的成员变量close是一个函数指针,它用来关闭一个硬件设备。
注意
硬件抽象层中的硬件设备是由其所在的模块提供接口来打开的,而关闭则是由硬件设备自身提供接口来完成的。
至此,硬件抽象层模块接口的编写规范就介绍完了。接下来,我们就可以为虚拟硬件设备freg编写硬件抽象层模块接口了。
-
加载硬件抽象层模块的过程实际上就是调用dlopen函数来加载与其对应的动态链接库文件的过程。在调用dlclose函数来卸载这个硬件抽象层模块时,要用到这个句柄值,因此,我们在加载时需要将它保存起来。