上接Android HAL:helloworld例程继续深入点学习。
Android HAL:helloworld展示了编写HAL的一个架构。因为HAL最终会被编译层一个.so库文件被Native层调用,那么Native是如何找到这个.so,并打开这个.so呢?
其实Native是通过调用hardware/libhardware/hardware.c中的已实现函数完成查找so和打开so的。
hardware.c
/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <hardware/hardware.h> #include <cutils/properties.h> #include <dlfcn.h> #include <string.h> #include <pthread.h> #include <errno.h> #include <limits.h> #define LOG_TAG "HAL" #include <utils/Log.h> /** 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", #ifdef OMAP_ENHANCEMENT "ro.product.processor", #endif "ro.board.platform", "ro.arch" }; static const int HAL_VARIANT_KEYS_COUNT = (sizeof(variant_keys)/sizeof(variant_keys[0])); /** * Load the file defined by the variant and if successful * return the dlopen handle and the hmi. * @return 0 = success, !0 = failure. */ static int load(const char *id, 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. */ 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; } int hw_get_module_by_class(const char *class_id, const char *inst, 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]; char name[PATH_MAX]; if (inst) snprintf(name, PATH_MAX, "%s.%s", class_id, inst); else strlcpy(name, class_id, 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_PATH2, name, prop); if (access(path, R_OK) == 0) break; snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH1, name, prop); if (access(path, R_OK) == 0) break; } else { 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) { /* load the module, if this fails, we're doomed, and we should not try * to load a different variant. */ status = load(class_id, path, module); } return status; } int hw_get_module(const char *id, const struct hw_module_t **module) { return hw_get_module_by_class(id, NULL, module); }Native通过调用hw_get_module函数打开.so,然后并得到返回的扩展的hw_module_t结构体。这里的调用过程是:
hw_get_module()->hw_get_mudule_by_class()->load()
注意load()函数中有一个dlopen()调用,这就是传统的加载打开动态库的函数啊!!!
Native层加载HAL .so的例程:
这里还是以Android HAL:helloworld例程为例
定义模块结构体(hw_module_t),执行hw_get_module函数:
hw_module_t * module; hw_get_module(HELLO_HARDWARE_MODULE_ID,&module);通过以上程序片段得到hw_module_t *module,注意helloworld模块module扩展结构体为
struct hello_module_t { struct hw_module_t common; };
struct hello_module_t HAL_MODULE_INFO_SYM = { common: { tag: HARDWARE_MODULE_TAG, version_major: 1, version_minor: 0, id: HELLO_HARDWARE_MODULE_ID, name: MODULE_NAME, author: MODULE_AUTHOR, methods: &hello_module_methods, } };所以通过hw_get_module得到的模块module指向的地址就是hello_module_t的地址(这也是为什么编写HAL时google要强制模块扩展结构体的第一个成员要是hw_module_t的原巧妙之处,见hardware.h说明)。
定义设备结构体(hw_device_t),调用模块方法(hw_module_methods_t)
struct hw_device_t *device; module->methods(module,"hello",&device);
methods函数实际上就是hello_device_open函数,通过以上程序片段得到hw_device_t *device,注意helloworld设备device扩展结构体为
struct hello_device_t { struct hw_device_t common; int fd; int (*set_val)(struct hello_device_t* dev, int val); int (*get_val)(struct hello_device_t* dev, int* val); };
所以通过调用模块方法得到设备device指向的地址就是hello_device_t的地址(这也是为什么编写HAL时google要强制设备扩展结构体的第一个成员要是hw_device_t的原巧妙之处,见hardware.h说明)
得到hello_device_t结构体后,就可以调用hello_device_t里面实现方法,再看回hello_device_t的定义:
struct hello_device_t { struct hw_device_t common; int fd; int (*set_val)(struct hello_device_t* dev, int val); int (*get_val)(struct hello_device_t* dev, int* val); };这里实现了set_val和get_val。
所以总结有以下的一点认识结论:
1.HAL与Linux内核driver交互,HAL层充当传统Linux的App角色,通过系统调用访问驱动,并形成一个供上层(Native)调用的接口。
2.HAL里面的扩展设备结构体xxx_hw_device_t里面定义的方法就是供上层调用的接口,是上层的数据通道。
3.HAL里面的模块结构体hw_module_t需实现open函数。
4.Native层费了很大劲通过hw_get_module、module->hw_module_t->methods等等调用目的是想获扩展设备结构体xxx_hw_device_t中实现的接口方法。
总结:
一个HAL基本实现概括为如下:
1.定义一个扩展的hw_module_t模块接口xxx_hw_module_t(helloworld例程定义为:hello_module_t)。
2.定义一个扩展的hw_device_t设备接口xxx_hw_device_t(helloworld例程定义为:hello_device_t)。
3.Native通过hw_get_module获得xxx_hw_module_t。
4.Native再通过xxx_hw_module_t里面的hw_module_t的methods方法(即open方法)获得xxx_hw_device_t。
5.Native获得xxx_hw_device_t后,即可调用xxx_hw_device_t里面实现的方法。