硬件模块库的通用写法
将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
原创文章,版权©红狼博客所有, 转载随意,但请注明出处。