Android HAL:分析与总结

来源:互联网 发布:.com英文域名 编辑:程序博客网 时间:2024/05/22 05:25

上接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里面实现的方法。








原创粉丝点击