每一个HAL模块都有一个ID值,以这些ID值为参数来调用硬件抽象层提供的函数hw_get_module就可以将
指定的模块加载到内存来,并且获得 一个hw_module_t接口来打开相应的设备。 函数hw_get_module实现在
hardware/libhardware/hardware.c文件中,如下所示:
1. #define HAL_LIBRARY_PATH1 "/system/lib/hw"
2. #define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
3. static const char *variant_keys[] = {
4. “ro.hardware”,
5. “ro.product.board”,
6. “ro.board.platform”,
7. “ro.arch”
8. };
9. // 由上面定义的字符串数组可知,HAL_VARIANT_KEYS_COUNT的值为4
10. struct constint HAL_VARIANT_KEYS_COUNT = (sizeof(variant_keys)/sizeof(variant_keys[0]));
11.
12. int hw_get_module(const char *id, const struct hw_module_t **module){
13. return hw_get_module_by_class(id, NULL, module);
14. }
15.
16. int hw_get_module_by_class(const char *class_id, const char *inst,
17. const struct hw_module_t **module){
18. int status;
19. int i;
20. // 声明一个hw_module_t指针变量hmi
21. const struct hw_module_t *hmi = NULL;
22. char prop[PATH_MAX};
23. char path[PATH_MAX];
24. char name[PATH_MAX];
25. // 由前面调用函数可知,inst = NULL,执行else部分,将硬件id名拷贝到name数组里
26. if(inst)
27. snprintf(name, PATH_MAX, “%s.%s”, class_id, inst);
28. else
29. strlcpy(name, class_id, PATH_MAX);
30. // i 循环5次
31. for(i=0; i
32. if(i
33. // 从系统属性里依次查找前面定义的4个属性的值,找其中一个后,执行后面代码,找不到,进入else部分执行
34. if(property_get(variant_keys[i], prop, NULL) == 0){
35. continue;
36. }
37. // 找到一个属性值prop后,拼写path的值为:/vendor/lib/hw/硬件id名.prop.so
38. snprintf(path, sizeof(path), “%s/%s.%s.so”,
39. HAL_LIBRARY_PATH2, name, prop);
40. if(access(path, R_OK) ==0) break; // 如果path指向有效的库文件,退出for循环
41. // 如果vendor/lib/hw目录下没有库文件,查找/system/lib/hw目录下有没有:硬件id名.prop.so的库文件
42. snprintf(path, sizeof(path), “%s/%s.%s.so”,
43. HAL_LIBRARY_PATH1, name, prop);
44. If(access(path, R_OK) == 0) break;
45. } else {
46. // 如果4个系统属性都没有定义,则使用默认的库名:/system/lib/hw/硬件id名.default.so
47. snprintf(path, sizeof(path), “%s/%s.default.so”,
48. HAL_LIBRARY_PATH1, name);
49. If(access(path, R_OK) == 0) break;
50. }
51. }
52. status = -ENOENT;
53. if(i
54. status = load(class_id, path, module); // 难道是要加载前面查找到的so库??
55. }
56. return status;
57. }
58.
函数hw_get_module_by_class依次在目录/system/lib /hw和/vendor/lib/hw中查找一个名称为"
gralloc.
gralloc.
gralloc.
gralloc.
只要其中的一个文件存在, 函数hw_get_module就会停止查找过程,并且调用另外一个函数load来将这个文件加载到
内存中来。另一方面,如果在/system/lib/hw和/vendor/lib/hw中均不存这些文件,那么函数hw_get_module就会在目录
/system/lib/hw中查找是否存在一个名称为gralloc.default.so的文件。如果存在的话,那么也会调用函数load将它加载到内
存中来,如下所示:
1. static int load(const char *id, counst char *path, const struct hw_module_t **pHmi){
2. void *handle;
3. struct hw_module_t * hmi;
4. // 通过dlopen打开so库
5. handle = dlopen(path, RTLD_NOW);
6. // sym的值为”HMI”
7. const char * sym = HAL_MODULE_INFO_SYM_AS_STR;
8. // 通过dlsym从打开的库里查找”HMI”这个符号,如果在so里有定义的函数名或变量名为HMI,dlsym返回其地址hmi,将该地址转化成hw_module_t类型,即硬件对象
9. hmi = (struct hw_module_t *)dlsym(handle, sym);
10. // 判断找到的硬件对象的id是否和要查找的id名一致,不一致出错退出
11.
12. if(strcmp(id, hmi->id) != 0){
13. // 出错退出处理
14. }
15. // 将库的句柄保存到hmi硬件对象的dso成员里
16. hmi->dso = handle;
17. // 将硬件对象地址送给load函数者,最终将硬件对象返回到了hw_get_module的调用者
18. *pHmi = hmi;
19. // 成功返回
20. }
在Linux系统中,后缀名为"so"的文件为动态链接库文件,可能通过函数dlopen来加载到内存中。硬件抽象层模块编写规范规定每
一个硬件抽象层模块都必须导出一个符号名称为HAL_MODULE_INFO_SYM_AS_STR的符号,而且这个符号必须是用来描述一个类型为
hw_module_t的结构体的。
HAL_MODULE_INFO_SYM_AS_STR是一个宏,定义在文件hardware/libhardware/include/hardware/hardware.h文件中,如下所示:
1. #define HAL_MODULE_INFO_SYM_AS_STR "HMI"
将模块加载到内存中来之后,就可以调用函数dlsym来获得它所导出的符号HMI。由于这个符号指向的是一个hw_module_t结构体,
因此,最后函数load就可以强制地将这个符号转换为一个hw_module_t结构体指针,并且保存在输出参数pHmi中返回给调用者。