每一個HAL模塊都有一個ID值,以這些ID值為參數來調用硬件抽象層提供的函數hw_get_module就可以將指定的模塊加載到內存來,並且獲得 一個hw_module_t接口來打開相應的設備。 函數hw_get_module實現在hardware/libhardware /hardware.c文件中,如下所示:
/** Base path of the hal modules */
- #define HAL_LIBRARY_PATH1 "/system/lib/hw"
- #define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
- /**
- * There are a set of variant filename for modules. The form of the filename
- * is "<MODULE_ID>.variant.so" so for the led module the Dream variants
- * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
- *
- * led.trout.so
- * led.msm7k.so
- * led.ARMV6.so
- * led.default.so
- */
- 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"
- };
- static const int HAL_VARIANT_KEYS_COUNT =
- (sizeof(variant_keys)/sizeof(variant_keys[0]));
- ......
- int hw_get_module(const char *id, const struct hw_module_t **module)
- {
- int status;
- int i;
- const struct hw_module_t *hmi = NULL;
- char prop[PATH_MAX];
- char path[PATH_MAX];
- /*
- * Here we rely on the fact that calling dlopen multiple times on
- * the same .so will simply increment a refcount (and not load
- * a new copy of the library).
- * We also assume that dlopen() is thread-safe.
- */
- /* Loop through the configuration variants looking for a module */
- for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
- if (i < HAL_VARIANT_KEYS_COUNT) {
- if (property_get(variant_keys[i], prop, NULL) == 0) {
- continue;
- }
- snprintf(path, sizeof(path), "%s/%s.%s.so",
- HAL_LIBRARY_PATH1, id, prop);
- if (access(path, R_OK) == 0) break;
- snprintf(path, sizeof(path), "%s/%s.%s.so",
- HAL_LIBRARY_PATH2, id, prop);
- if (access(path, R_OK) == 0) break;
- } else {
- snprintf(path, sizeof(path), "%s/%s.default.so",
- HAL_LIBRARY_PATH1, id);
- if (access(path, R_OK) == 0) break;
- }
- }
- status = -ENOENT;
- if (i < HAL_VARIANT_KEYS_COUNT+1) {
- /* load the module, if this fails, we're doomed, and we should not try
- * to load a different variant. */
- status = load(id, path, module);
- }
- return status;
- }
函數hw_get_module依次在目錄/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將它加載到內存中來。函數load也是實現在文件hardware/libhardware/hardware.c文件中,如下所示:
- const char *path,
- const struct hw_module_t **pHmi)
- {
- int status;
- void *handle;
- struct hw_module_t *hmi;
- /*
- * 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();
- LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
- status = -EINVAL;
- goto done;
- }
- /* Get the address of the struct hal_module_info. */
- 每個模塊定義了一個hw_module_t或hw_module_t子類型變量HAL_MODULE_INFO_SYM,通過這個導出符號可以得到hw_module_t變量對象。
- const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
- hmi = (struct hw_module_t *)dlsym(handle, sym);
- if (hmi == NULL) {
- LOGE("load: couldn't find symbol %s", sym);
- status = -EINVAL;
- goto done;
- }
- /* Check that the id matches */
- if (strcmp(id, hmi->id) != 0) {
- LOGE("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 {
- LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
- id, path, *pHmi, handle);
- }
- *pHmi = hmi;
- return status;
- }
在Linux系統中,后綴名為"so"的文件為動態鏈接庫文件,可能通過函數dlopen來加載到內存中。硬件抽象層模塊編寫規范規定每一個硬件抽象層模塊都必須導出一個符號名稱為HAL_MODULE_INFO_SYM_AS_STR的符號,而且這個符號必須是用來描述一個類型為hw_module_t的結構體的。
HAL_MODULE_INFO_SYM_AS_STR是一個宏,定義在文件hardware/libhardware/include/hardware/hardware.h文件中,如下所示:
- #define HAL_MODULE_INFO_SYM_AS_STR "HMI"
將模塊加載到內存中來之后,就可以調用函數dlsym來獲得它所導出的符號HMI。由於這個符號指向的是一個hw_module_t結構體,因此,最后函 數load就可以強制地將這個符號轉換為一個hw_module_t結構體指針,並且保存在輸出參數pHmi中返回給調用者。