3.2.2 硬件抽象层接口方式
1.hardware模块的方式
Android的libhardware库提供一种不依赖编译时绑定,可以动态加载硬件抽象层,硬件模块方式的硬件抽象层架构如图3-7所示。
图3-7 硬件模块方式的硬件抽象层
在libhardware的接口中定义了通用的内容和各个硬件模块的id,每一个硬件模块需要被实现成所要求的接口形式,并生成单独的动态库文件(*.so)。
在使用硬件抽象层的过程中,Android系统的框架层将调用libhardware的接口,根据每一个模块的id,将在指定路径动态打开(dlopen)各个硬件模块,然后找到符号(dlsym),调用硬件模块中的各个接口。
接口的调用方式是一致的,只是在不同系统所打开的硬件模块是不相同的。
Android仿真器的环境中,包含的硬件模块如下所示:
# ls /system/lib/hw sensors.goldfish.so gralloc.default.so
其中,gralloc.default.so表示默认的Gralloc模块(用于显示),sensors.goldfish.so表示默认的传感器模块。
libhardware的接口在以下目录中定义:
hardware/libhardware/include/hardware/hardware.h
struct hw_module_t结构体用于定义了硬件模块的格式,如下所示:
typedef struct hw_module_t { uint32_t tag; /* tag,需要被初始化为HARDWARE_MODULE_TAG */ uint16_t version_major; /* 主版本号 */ uint16_t version_minor; /* 次版本号*/ const char *id; /* 模块标识 */ const char *name; /* 模块的名称 */ const char *author; /* 模块作者 */ struct hw_module_methods_t* methods; /* 模块方法 */ void* dso; /* 模块的dso */ uint32_t reserved[32-7]; /* 填充字节,为以后使用 */ } hw_module_t;
struct hw_module_t结构体定义了一个硬件模块的信息。在各个具体硬件模块中,需要以这个结构体为第一个成员,即表示“继承”了这个结构体。
struct 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;
struct hw_module_methods_t结构体只包含了一个打开模块的函数指针,这个结构体也作为struct hw_module_t结构体的一个成员。
struct hw_device_t表示一个硬件设备,如下所示
typedef struct hw_device_t { uint32_t tag; /* tag 需要被初始化为HARDWARE_DEVICE_TAG */ uint32_t version; /* hw_device_t的版本号 */ struct hw_module_t* module; /* 引用这个设备属于的硬件模块 */ uint32_t reserved[12]; /* 填充保留字节 */ int (*close)(struct hw_device_t* device); /* 关闭设备 */ } hw_device_t;
struct hw_device_t也是需要被具体实现的结构体包含使用的,一个硬件模块可以包含多个硬件设备。
硬件的具体的调用流程如下所示:
(1)通过id得到硬件模块。
(2)从硬件模块中得到hw_module_methods_t,打开得到硬件设备hw_device_t。
(3)调用hw_device_t中的各个方法(不同模块所实现的)。
(4)调用方程,通过hw_device_t的close关闭设备。
模块打开到使用的流程,如图3-8所示。
图3-8 硬件模块方式的调用方式
在以上的流程中,还需要libhareware提供一个得到模块的函数,各个函数就是hw_get_module,如下所示:
int hw_get_module(const char *id, const struct hw_module_t **module);
hw_get_module()函数的实现在hardware/libhardware/目录的hardware.c文件中,其内容如下所示:
int hw_get_module(const char *id, const struct hw_module_t **module) { int status; int i; const struct hw_module_t *hmi = NULL; char prop[PATH_MAX]; char path[PATH_MAX]; for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) { if (i < HAL_VARIANT_KEYS_COUNT) { if (property_get(variant_keys[i], prop, NULL) == 0) { continue; } snprintf(path, sizeof(path), "%s/%s.%s.so", // 得到模块的名称 HAL_LIBRARY_PATH, id, prop); } else { snprintf(path, sizeof(path), "%s/%s.default.so", // 得到默认模块的名称 HAL_LIBRARY_PATH, id); } if (access(path, R_OK)) { continue; } break; // 找到模块,然后退出 } status = -ENOENT; if (i < HAL_VARIANT_KEYS_COUNT+1) { status = load(id, path, module); } return status; }
hw_get_module()函数的内容可见,执行的是一个动态查找的过程,找到硬件动态库(*.so)打开,当没有动态库的时候,将打开默认的库文件(*.default.so)。
在hw_get_module()函数中调用的load()函数,其主要内容如下所示:
static int load(const char *id, const char *path, const struct hw_module_t **pHmi) { int status; void *handle; struct hw_module_t *hmi; handle = dlopen(path, RTLD_NOW); /* 进行动态库的打开 */ const char *sym = HAL_MODULE_INFO_SYM_AS_STR; hmi = (struct hw_module_t *)dlsym(handle, sym); /* ……省略部分内容 */ }
load()函数实际上执行了一个动态的打开(dlopen)和动态取出符号(dlsym)的过程。这个过程解除了在编译阶段的Android本地框架对特有的硬件模块依赖。
硬件模块的调用方式如下所示:
xxx_module_t * gModule; xxx_device_t * gDevice; { xxx_module_t const* module; err = hw_get_module(XXXX_HARDWARE_MODULE_ID, (const hw_module_t **)&module); if (err == 0) gModule = (xxxx_module_t*)module; gModule->ModuleFunction(); /* 调用模块中的函数 */ gDevice->DeviceFunction(); /* 调用设备中的函数 */ }
通常情况下,硬件模块的调用者是Android中的本地框架层。
libhardware的接口头文件中,除了hardware.h之外,其他各个头文件是相互并列的,每一个文件表示了一种硬件抽象层。
lights.h(背光和指示灯模块)
copybit.h(位复制模块)
overlay.h(叠加视频抽象层模块)
qemud.h(QEMU的守护进程模块)
sensors.h(传感器模块)
gralloc.h(gralloc模块,用于显示,Eclair版本新增)
2.直接接口方式
hardware_legacy库中提供了一些各自独立的接口,由用户实现后形成库,被直接连接到系统中。这是实现硬件抽象层最简单也是最直接的方式。hardware_legacy的头文件路径为:
hardware/libhardware_legacy/include/hardware_legacy
Android中的蓝牙库bluedroid与之类似,也是采用同样的方式,其头文件的路径为:
system/bluetooth/bluedroid/include/bluedroid/bluetooth.h
hardware_legacy库中包含了几个C接口的文件,如GPS ,power,wifi,vibrator等,同时在hardware_legacy库中也包含了power,wifi,vibrator这几个系统的实现和GPS在仿真器上的实现。在开发一个新的硬件系统时,可以根据需要去实现这几个库,也可以使用系统默认的实现方式。
这种做法实际上并没有完全将硬件抽象层和Android的本地框架分开,其好处是接口的定义和实现比较简单。
3.C++的继承实现方式
使用C++类的继承方式实现硬件抽象层,也是Android中的一种方式。为了使用这种方式,Android平台定义了C++的接口。由具体的实现者继承实现这些接口,同时在Android系统中,通常也有通用的实现方式,可以作为一个简易的实现或者“桩(stub)”的作用。
使用C++类的继承方式的硬件抽象层结构如图3-9所示。
图3-9 使用C++类的继承方式的硬件抽象层结构
在这种实现方式中,具体的硬件抽象层通常要求被编译成为指定的名称的动态库,由本地框架库连接它;通用的实现被编译成静态库(*.a),本地框架库连接这些静态库的时候,其实就是包含了它们在其中。使用特定硬件抽象层还是通用的硬件抽象层,通常需要根据宏来指定。
Camera和Audio系统使用的是C++类的继承的方式。
4.直接调用驱动
在Android中有一些比较简单的子系统,并没有在“物理”上存在的硬件抽象层,也就是说,实现其硬件抽象功能的部分不在单独的代码中。例如,由JNI部分代码直接调用的驱动程序的设备节点或者使用sys文件系统。
Alarm子系统就是采用的这种方式,在JNI中直接调用驱动程序。此外,在Android的JNI代码中,还包含一些对sys文件系统的访问,例如switch等。