对于HAL怎么捕获到上接所说的上传数据
参考前面的 https://blog.csdn.net/qq_42856778/article/details/89374880
一问
我们在下层写好的每一个模块结构的名字都是HAL_MODULE_INFO_SYM,怎么精准调用到 此时需要的模块?靠什么标志来识别(id? name? addr?)
二问、
在前面我们将向上提供的接口做成一个动态库,那么这个动态库怎么被调用?
这个动态库应该放在什么地方?
在 android/hardware/libhardware/include/hardware/hardware.h里面还出现了两个函数,根据他们的函数
名字很明显得出他们的作用。其实就是两个对外的接口
1、/**int hw_get_module() **/
int hw_get_module(const char *id, const struct hw_module_t **module) {
return hw_get_module_by_class(id, NULL, module);
}
单看这个函数的名字,是获取模块的一个函数。
可以看到hw_get_module直接调用hw_get_module_by_class,在之前我们自定义模块结构(Test_module_t)
有着两个要求,名字必须是HAL_MODULE_INFO_SYM和第一个成员必须是 hw_module_t ,可以看得出都是共同的接口,
这样就出现前面的一问?
答案:在 struct hw_module_t 结构体里面有一个成员叫做id通过其备注可以知道。通过共同接口进来之后,
查找对应的模块就是通过ID查找(必须是独一无二的)。其实就是通过ID精准地查找到定义的模块。
那么hw_get_module()的第二个形参猜得到了,它就是一个篮子,将我们下层定义的模块装上来。
总结:就是通过ID找到模块,在将整个模块都获取上来。
现在当我们的id传给hw_get_module_by_class()函数时会怎么操作呢?一个ID怎么辗转才能对应的模块。
2、
int hw_get_module_by_class (class_id,inst,module) 函数原型和里边具体操作:
int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module){
int i = 0;
char path[PATH_MAX] = {0};
char name[PATH_MAX] = {0};
char prop_name[PATH_MAX] = {0};
if (inst)
snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
else
trlcpy(name, class_id, PATH_MAX);
snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
if (property_get(prop_name, prop, NULL) > 0) {
if (hw_module_exists(path, sizeof(path), name, prop) == 0) { goto found;}
}
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
if (property_get(variant_keys[i], prop, NULL) == 0) { continue; }
if (hw_module_exists(path, sizeof(path), name, prop) == 0) { goto found; }
}
/* Nothing found, try the default */
if (hw_module_exists(path, sizeof(path), name, "default") == 0) { goto found; }
return -ENOENT;
found:
return load(class_id, path, module);
}
当inst参数为空的时候传进来的id,就会被拷贝,然后被 snprintf()封装[ro.hardware.name},
封装成只读属性(属性详细①),通过property_get()查找这个属性,判断是否配置项定义了对应的HAL层,
对应的HAL层存在,则通过hw_module_exists(path,path_len,name,subname)去共享库目录下查找对应的so库。
并且获取到库的路径。找到了跳去执行load(class_id, path, module);
3、*
解析上面的hw_module_exists()
函数揭露了,有id就去HAL层生成的库。这个函数其实里面主要的三个判断处理,
每一个判断代表一个路径,即是在三个路径判断库是否存在.(hardware.c标明)
#if defined(__LP64__)
#define HAL_LIBRARY_PATH1 "/system/lib64/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib64/hw"
#else
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib/hw"
#endif
int hw_module_exists(char *path, size_t path_len, const char *name, const char *subname) {
snprintf(path, path_len, "%s/%s.%s.so",HAL_LIBRARY_PATH3, name, subname);
if (access(path, R_OK) == 0){return 0;}
// /vendor/lib/hw/name.subname.so或者/vendor/lib64/hw/name.subname.so
snprintf(path, path_len, "%s/%s.%s.so",HAL_LIBRARY_PATH2, name, subname);
if(access(path, R_OK) == 0){ return 0; }
// /system/lib/hw/name.subname.so或者/system/lib64/hw/name.subname.so
snprintf(path, path_len, "%s/%s.%s.so",HAL_LIBRARY_PATH1, name, subname);
if(access(path, R_OK) == 0) { return 0; }
return -ENOENT;
}
在 hw_get_module_by_class (class_id,inst,module)里面还这么一段代码,
当其实里面讲的是在配置变量中查找,属性值是否存在,存在还是通过
hw_module_exists()
去共享库目录下查找对应的so库。并且获取到库的路径。获取到就回去执行
load(class_id, path, module);
如果经过查找之后都没有属性值,返回一个错误值-ENOENT结束退出。
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
if (property_get(variant_keys[i], prop, NULL) == 0) { continue; }
if (hw_module_exists(path, sizeof(path), name, prop) == 0) { goto found;}
}
配置变量:
static const char *variant_keys[] = {
"ro.hardware", /* This goes first so that it can pick up a different
file on the emulator. */
"ro.product.board",
"ro.board.platform",
"ro.arch"
};
在上面配置变量查找到属性值的时候都会跳到 load(class_id, path, module)函数里面,分析这个函数
load(id,path,pHmi)。
在hw_get_module_by_class()函数里面看到,它三个形参对应的分别是,传进来的ID
获取到的库路径、module(在hw_get_module()所说的篮子)结构指针。下面分析下该函数
static int load(const char *id,const char *path,const struct hw_module_t **pHmi) {
int status = -EINVAL;
void *handle = NULL;
struct hw_module_t *hmi = NULL;
/*
* load the symbols resolving undefined symbols before
* dlopen returns. Since RTLD_GLOBAL is not or'd in with
* RTLD_NOW the external symbols will not be global
*/
handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
char const *err_str = dlerror();
ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
status = -EINVAL;
goto done;
}
/* Get the address of the struct hal_module_info. */
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_module_t *)dlsym(handle, sym);
if (hmi == NULL) {
ALOGE("load: couldn't find symbol %s", sym);
status = -EINVAL;
goto done;
}
/* Check that the id matches */
if (strcmp(id, hmi->id) != 0) {
ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
status = -EINVAL;
goto done;
}
hmi->dso = handle;
/* success */
status = 0;
done:
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
在整个函数里面我们会发现只有两个函数在里面工作,其他都是做一些出错判断。
dlopen(path, RTLD_NOW);
dlsym(handle, sym);
函数解析
dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个操作句柄给调用进程
参数:
path :动态库对应路径名字
RTLD_NOW :立即决定,返回前解除所有未决定的符号
返回值: 操作句柄
dlsym()函数 根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的地址。
使用这个函数不但可以获取函数地址,也可以获取变量地址。
参数
handle:由dlopen打开动态链接库后返回的指针;
symbol:要求获取的函数或全局变量的名称。
返回值:指向函数或全局变量的名称
那么整一个load(id,path,pHmi)函数做的事情就是,通过传进来的ID和path,查找到对应的动态
库文件,调用dlopen()将它打开,返回一个操作句柄,dlsym()拿到操作句柄,获取到变量的地址
也就是我们在下层定义好的 hw_module_t 模块的地址。然后赋值给hw_get_module()函数的module变量地址。
换句话说,我们所要做的任务就已经完成了。同时以上答案也会回答了第二个问题。
最后一问:
如果hw_get_module_by_class()的第二个函数不为空的情况下会是怎么样的呢?
if (inst)
snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
跟着代码走得到 name = class_id.inst 一个这样的结构,那么对于我们来说作用或者说
新的特点在哪里呢,我看了网上很多关于HAL这方便的博客资料。都未能找出一个相对满意的答案。解答的只有不断学习和使用才能解析心中更大的疑问。