美文网首页
Android硬件抽象层模块编写规范

Android硬件抽象层模块编写规范

作者: 雪狼老巫 | 来源:发表于2020-06-20 21:25 被阅读0次

    硬件抽象层模块编写规范

    硬件抽象层最终都会生成.so文件,放到系统对应的目录中。在系统使用的时候,系统会去对应目录下加载so文件,实现硬件抽象层的功能。因此硬件抽象层的加载过程就是我们使用so的一个接口。先了解加载过程从源头了解抽象层模块儿的编写规范。

    1、硬件抽象层加载过程

    系统在加载so的过程中,会去两个目录下查找对应id的so文件。这两个目录分别是/system/lib/hw和/vendor/lib/hw。

    so文件的名字分为两个部分例如id.prop.so,第一部分是模块id。第二部分是系统prop的值,获取顺序为“ro.hardware”、“ro.producat.board”、“ro.board.platform”、“ro.arch”,如果prop都找不到的话,就用default。(不是找不到prop的值,是找不到prop值对应的so文件)。

    负责加载硬件抽象层模块的函数是hw_get_module,所在的文件是/hardware/libhardware/hardware.c如下:

    /** Base path of the hal modules */

    #if defined(__LP64__)

    #define HAL_LIBRARY_PATH1 "/system/lib64/hw"

    #define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"

    #else

    #define HAL_LIBRARY_PATH1 "/system/lib/hw"

    #define HAL_LIBRARY_PATH2 "/vendor/lib/hw"

    #endif

    /**

     * There are a set of variant filename for modules. The form of the filename

    * is ".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]));

    /**

     * 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();

            ALOGE("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) {

            ALOGE("load: couldn't find symbol %s", sym);

            status = -EINVAL;

            goto done;

        }

        /* Check that the id matches */

        if (strcmp(id, hmi->id) != 0) {

            ALOGE("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 {

            ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",

                    id, path, *pHmi, handle);

        }

        *pHmi = hmi;

        return status;

    }

    /*

     * Check if a HAL with given name and subname exists, if so return 0, otherwise

     * otherwise return negative.  On success path will contain the path to the HAL.

     */

    static int hw_module_exists(char *path, size_t path_len, const char *name,

                                const char *subname)

    {

        snprintf(path, path_len, "%s/%s.%s.so",

                 HAL_LIBRARY_PATH2, name, subname);

        if (access(path, R_OK) == 0)

            return 0;

        snprintf(path, path_len, "%s/%s.%s.so",

                 HAL_LIBRARY_PATH1, name, subname);

        if (access(path, R_OK) == 0)

            return 0;

        return -ENOENT;

    }

    int hw_get_module_by_class(const char *class_id, const char *inst,

                               const struct hw_module_t **module)

    {

        int i;

        char prop[PATH_MAX];

        char path[PATH_MAX];

        char name[PATH_MAX];

        char prop_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.

         */

        /* First try a property specific to the class and possibly instance */

        snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);

        if (property_get(prop_name, prop, NULL) > 0) {

            if (hw_module_exists(path, sizeof(path), name, prop) == 0) {

                goto found;

            }

        }

        /* Loop through the configuration variants looking for a module */

        for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {

            if (property_get(variant_keys[i], prop, NULL) == 0) {

                continue;

            }

            if (hw_module_exists(path, sizeof(path), name, prop) == 0) {

                goto found;

            }

        }

        /* Nothing found, try the default */

        if (hw_module_exists(path, sizeof(path), name, "default") == 0) {

            goto found;

        }

        return -ENOENT;

    found:

        /* load the module, if this fails, we're doomed, and we should not try

         * to load a different variant. */

        return load(class_id, path, module);

    }

    int hw_get_module(const char *id, const struct hw_module_t **module)

    {

        return hw_get_module_by_class(id, NULL, module);

    }

    找到so文件之后,调用方法load方法去加载对应的so文件,并返回hw_module_t结构体。load方法源码在上面程序中。

    load方法首先调用dlopen加载对应的so文件到内存中。然后用dlsym方法找到变量HAL_MODULE_INFO_SYM_AS_STR符号对应的地址,这个地址也就是一个hw_module_t结构体,然后从这个结构体中拿出id比对load方法出入的id是否一致,如果是的话表示打开成功。加载过程完成。

    HAL_MODULE_INFO_SYM_AS_STR这个符号值为HMI,也就是必须要保证这个符号之后是一个hw_module_t。接下来的规范中有这个要求。

    到此,模块加载完成

    2、硬件抽象层模块编写规范

    硬件抽象层有两个结构体,一个是hw_module_t和hw_device_t,定义在hardware.h中。

    首先说一下hw_module_t的编写规范。

    1、必须要有一个“自定义硬件抽象层结构体”,且结构体第一个变量类型要为hw_module_t。

    2、必须存在一个HARDWARE_MODULE_INFO_TAG的符号,且指向“自定义硬件抽象层结构体”。在加载的时候根据这个符号找到地址,并把地址的转变为hw_module_t,这也是为什么第一条中hw_module_t必须要在第一个的原因。

    3、hw_module_t的tag必须为HARDWARE_MODULE_TAG

    4、结构体中要有一个方法列表,其中要有一个open方法。用open方法获得hw_device_t

    接下来说一下hw_device_t的编写规范

    1、必须要有一个“自定义硬件设备结构体”,且结构体第一个变量类型要为hw_device_t。

    2、hw_device_t的tag必须为HARDWARE_DEVICE_TAG

    3、要有一个close函数指针,来关闭设备

    按照上面规范编写的硬件抽象层就可以由系统加载并正确获取到device。具体的应用层逻辑在device中实现。

    相关文章

      网友评论

          本文标题:Android硬件抽象层模块编写规范

          本文链接:https://www.haomeiwen.com/subject/xedwxktx.html