Android源码分析:硬件适配层HAL(1)之总述

Android源码分析:硬件适配层HAL(1)之总述

红狼博客

硬件模块库的通用写法

Android移植到不同的硬件平台上,这些平台可能包含诸如GSenor、 CompassLightoverlay等。Android Framework通过JNI通过调用这些硬件模块库来使用操控这些硬件。Google已经为这些提供了一个完整的框架,移植开发者只需编写相应的函数即可,不需考虑太多与Android Framework如何交互,这样就可以让开发者将精力集中在硬件的操控上。

在头文件hardware/libhardware/include/hardware/hardware.h中,定义了通用的数据结构。主要包括:hw_module_thw_device_t两个结构体。前者代表着硬件模块,包含硬件模块的一些基本信息(诸如版本号和开发者,详见下面的代码注释)和一个成员函数结构体(里面只是一个打开硬件设备的open函数指针,这需要开发者去实现函数);后者代表着硬件,它里面包含了一个关闭硬件的close函数指针。硬件开发者在开发自己的模块时,需要在代码中给结构体变量赋值,包括将自己实现的函数赋给函数指针。

hw_module_thw_device_t和类型如下:

//硬件模块结构体类型:
struct hw_module_t {
uint32_t tag; // tag
标识:应初始化为宏HARDWARE_MODULE_TAG定义的‘HWMT’
uint16_t version_major; //
模块主版本号
uint16_t version_minor;//
模块次版本号
const char *id; //
模块ID
const char *name;//
模块名称
const char *author; //
模块开发者
struct hw_module_methods_t* methods; //
函数结构体,相当于C++中的成员函数
uint32_t reserved[32-6];//
保留的填充字节
};

//函数结构体只包含一个打开具体的设备的函数,定制开发者开发的模块库中应实现它
struct hw_module_methods_t {
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
};

//硬件设备结构体:
struct hw_device_t {
uint32_t tag; //Tag
标记,必须初始化为宏HARDWARE_DEVICE_TAG定义的‘HWDT’
uint32_t version; //
版本号
struct hw_module_t* module; //
对应的硬件模块指针
uint32_t reserved[12]; //
保留字段
int (*close)(struct hw_device_t* device);//
关闭设备的函数指针
};


//
成功调用则返回值为0,获取的模块存放在module中;失败则返回值为负,module设为空
int hw_get_module(const char *id, const struct hw_module_t **module);

如上代码注释,还声明了一个hw_get_module函数,用于从开发者编写的硬件模块库中获取hw_module_t模块变量符号,以给JNI层使用。下面是hardware/libhardware/hardware.c

的代码及其注释:

//硬件模块放置的路径
#define HAL_LIBRARY_PATH “/system/lib/hw”

//缺省变种名称
#define HAL_DEFAULT_VARIANT “default”

//变种搜寻顺序,数组中的值是属性键标识,属性值则是变种名称
static const char *variant_keys[] = {
“ro.hardware”, //
在模拟器上该值被设置,因此使用它
“ro.product.board”,
“ro.board.platform”,
“ro.arch”
};

//数组元素个数
#define HAL_VARIANT_KEYS_COUNT (sizeof(variant_keys)/sizeof(variant_keys[0]))

//装载具体的模块文件,成功返回值为0;非0值则失败,解析的模块符号名称存在pHmi.
static int load(const char *id, const char *variant,
const struct hw_module_t **pHmi)
{
int status;
void *handle;
const struct hw_module_t *hmi;
char path[PATH_MAX];

//得到形如“/system/lib/hw/led.trout.so”字符串:
snprintf(path, sizeof(path), “%s/%s.%s.so”, HAL_LIBRARY_PATH, id, variant);

LOGV(“load: E id=%s path=%s”, id, path);

handle = dlopen(path, RTLD_NOW);//打开硬件模块库

if (handle == NULL) {//错误检查
char const *err_str = dlerror();
LOGW(“load: module=%s error=%s”, path, err_str);
status = -EINVAL;
goto done;
}

const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (const struct hw_module_t *)dlsym(handle, sym);//
解析宏定义的符号
if (hmi == NULL) {
char const *err_str = dlerror();
LOGE(“load: couldn’t find symbol %s”, sym);
status = -EINVAL;
goto done;
}

if (strcmp(id, hmi->id) != 0) {//检查是ID名称是否否匹配
LOGE(“load: id=%s != hmi->id=%s”, id, hmi->id);
status = -EINVAL;
goto done;
}

//成功后的返回值:
status = 0;

done:
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
}

*pHmi = hmi;//解析后的符号指针将通过它传回

LOGV(“load: X id=%s path=%s hmi=%p handle=%p status=%d”,id, path,
*pHmi, handle, status);

return status;
}

//根据模块名称ID进行装载:先判断采取哪个变种,然后调用load装载模块文件解析符号
int hw_get_module(const char *id, const struct hw_module_t **module)
{
int status;
int i;
const struct hw_module_t *hmi = NULL;
LOGV(“hal_module_info_get: Load module id=%s”, id);
status = -EINVAL;

//依优先级顺序获取模块
for (i = 0; (status != 0) && (i < HAL_VARIANT_KEYS_COUNT); i++) {
if (property_get(variant_keys[i], prop, NULL) == 0) {
continue;
}
status = load(id, prop, &hmi);
}

//如果没有指定,装载哪个变种则装载缺省
if (status != 0) {
status = load(id, HAL_DEFAULT_VARIANT, &hmi);//
若没指定,则使用缺省
}

*module = hmi; //模块结构体指针通过它传回给调用者

LOGV(“hal_module_info_get: X id=%s hmi=%p status=%d”, id, hmi, status);
return status;
}

JNI层使用函数hw_get_module去打开模块文件库文件,然后解析库中的符号HAL_MODULE_INFO_SYM(宏定义为“HMI”),获取其变量指针,在通过一些检查后,可以调用它的open函数打开设备,然后进行相应的操作。

注意:hw_get_module装载一个.so模块库时,该库文件名称形如:

“<MODULE_ID>.<Variant>.so”

其中“MODULE_ID”指的是模块ID名称,如lightsled等。

”Variant”指定了变种。同一架构下会有不同的平台,同一平台会有不同的产品系列,比如HTC Dream 手机上的led灯模块,将依次根据属性系统中是否指定有值:productplatformarchtecture版本:

led.trout.so trout产品上的版本

led.msm7k.so: 高通msm7xxx平台上的版本

led.ARMV6.soARMV6版本

led.default.so:缺省版本

可以在Android的属性系统中指定它们:“ro.product.board”“ro.board.platform”“ro.arch”。于是,在函数hw_get_module装载哪个模块文件时,依次判断是否指定有值:若有,则使用它,若没有则继续搜寻下去,都没有,则使用缺省。

在下一节将以Android中的overlay原始代码为例讲述如何编写硬件模块。

本文链接地址: http://www.redwolf-blog.com/?p=916

原创文章,版权©红狼博客所有, 转载随意,但请注明出处。

    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/maochengtao/article/details/34055375
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞