每一個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<HAL_VARIANT_KEYS_COUNT+1; i++){
32. if(i<HAL_VARIANT_KEYS_COUNT){
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<HAL_VARIANT_KEYS_COUNT+1){
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中查找一個名稱爲"<MODULE_ID>.variant.so"的文件,其 中,<MODULE_ID>是一個模塊ID,而variant表 示"ro.hardware"、"ro.product.board"、"ro.board.platform"和"ro.arch"四個系統屬性值之一。例如,對於Gralloc模塊來說,函數hw_get_module依次在目錄/system/lib/hw和/vendor/lib/hw中檢查是否存在以下四個文件:
gralloc.<ro.hardware>.so
gralloc.<ro.product.board>.so
gralloc.<ro.board.platform>.so
gralloc.<ro.arch>.so
只要其中的一個文件存在, 函數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中返回給調用者。