您的位置:首页 > 移动开发 > Android开发

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

2014-06-24 08:43 411 查看
Android源码分析:硬件适配层HAL(1)之总述

红狼博客

硬件模块库的通用写法

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

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

hw_module_t和hw_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名称,如lights或led等。

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

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

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

led.ARMV6.so:ARMV6版本

led.default.so:缺省版本

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

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

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

原创文章,版权©红狼博客所有, 转载随意,但请注明出处。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: