通过hw_get_module_t来找到注册的硬件对象。
@hardware/libhardware/hardware.c
static const char *variant_keys[] = {
“ro.hardware”,
“ro.product.board”,
“ro.board.platform”,
“ro.arch”
};
// 由上面定义的字符串数组可知,HAL_VARIANT_KEYS_COUNT的值为4
struct constint HAL_VARIANT_KEYS_COUNT =
(sizeof(variant_keys)/sizeof(variant_keys[0]));
int hw_get_module(const char *id, const struct hw_module_t **module)
{
// 调用3个参数的hw_get_module_by_class函数
return hw_get_module_by_class(id, NULL, module);
}
int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module){
int status;
int i;
// 声明一个hw_module_t指针变量hmi
const struct hw_module_t *hmi = NULL;
char prop[PATH_MAX};
char path[PATH_MAX];
char name[PATH_MAX];
// 由前面调用函数可知,inst = NULL,执行else部分,
// 将硬件id名拷贝到name数组里
if(inst)
snprintf(name, PATH_MAX, “%s.%s”, class_id, inst);
else
strlcpy(name, class_id, PATH_MAX);
// i 循环5次
for(i=0; i<HAL_VARIANT_KEYS_COUNT+1; i++){
if(i<HAL_VARIANT_KEYS_COUNT){
/*
从系统属性里依次查找前面定义的4个属性的值,找其中一个后,
执行后面代码,找不到,进入else部分执行
*/
if(property_get(variant_keys[i], prop, NULL) == 0){
continue;
}
/*
找到一个属性值prop后,拼写path的值为:
/vendor/lib/hw/硬件id名.prop.so
*/
snprintf(path, sizeof(path), “%s/%s.%s.so”,
HAL_LIBRARY_PATH2, name, prop);
if(access(path, R_OK) ==0) break;
/* 如果path指向有效的库文件,退出for循环,
如果vendor/lib/hw目录下没有库文件,
查找/system/lib/hw目录下有没有:硬件id名.prop.so的库文件,
*/
snprintf(path, sizeof(path), “%s/%s.%s.so”,
HAL_LIBRARY_PATH1, name, prop);
if(access(path, R_OK) == 0) break;
} else {
/* 如果4个系统属性都没有定义,则使用默认的库名:
/system/lib/hw/硬件id名.default.so
*/
snprintf(path, sizeof(path), “%s/%s.default.so”,
HAL_LIBRARY_PATH1, name);
If(access(path, R_OK) == 0) break;
}
}
status = -ENOENT;
if(i<HAL_VARIANT_KEYS_COUNT+1){
status = load(class_id, path, module);
/*
加载前面查找到的so库。。
*/
}
return status;
}
static int load(const char *id, counst char *path,
const struct hw_module_t **pHmi){
void *handle;
struct hw_module_t * hmi;
// 通过dlopen打开so库
handle = dlopen(path, RTLD_NOW);
// sym的值为”HMI”,这个名字还有印象吗?
const char * sym = HAL_MODULE_INFO_SYM_AS_STR;
/* 通过dlsym从打开的库里查找”HMI”这个符号,如果在so代码里有定义的函数名
或变量名为HMI,dlsym返回其地址hmi,将该地址转化成hw_module_t类型,
即,硬件对象,这招够狠,“杀鸡取卵”
*/
hmi = (struct hw_module_t *)dlsym(handle, sym);
/*
判断找到的硬件对象的id是否和要查找的id名一致,不一致出错退出
*/
if(strcmp(id, hmi->) != 0){
// 出错退出处理
}
/* 将库的句柄保存到hmi硬件对象的dso成员里 */
hmi->dso = handle;
/*
将硬件对象地址送给 load 函数者,
最终将硬件对象返回到了 hw_get_module 的调用者
*/
*pHmi = hmi;
// 成功返回
}
通过上面代码的注释分析可知,硬件对象声明的结构体代码被编译成了so库,由于该结构体声明为const类型,被so库包含在其静态代码段里。
要找到硬件对象,首先要找到其对应的so库,再通过 dlopen,dlsym 这种“杀鸡取卵”的方式找到硬件对象。
在声明结构体led_module_t时,其名字统一定义为了HMI,而这么做的目的就是为了通过dlsym来查找led HAL Stub源码生成的so库里的”HMI”符号。现在很明显了,我们写的HAL Stub代码最终要编译so库文件,并且库文件名为:led.default.so(当然可以设置四个系统属性之一来指定名字为:led.属性值.so),并且库的所在目录为:/system/lib/hw/。
参考大神博客,按照自己理解习惯整理,参考链接如下:
https://outfox.blog.csdn.net/article/details/8074549