Android HAL:分析与总结

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








你可能感兴趣的:(Android HAL:分析与总结)