Android源码分析二 硬件抽象层(HAL)

一 什么是HAL

  HAL 可定义一个标准接口以供硬件供应商实现,这可以让 Android 忽略较低级别的驱动程序实现。借助 HAL,您能够顺利实现相关功能,而不会影响或更改更高级别的系统。HAL 实现会被封装成模块,并由 Android 系统适时地加载。html

  硬件抽象层是介于android内核kernel和上层之间的抽象出来的一层结构。他是对linux驱动的一个封装,对上层提供统一接口,上层应用没必要知道下层硬件具体怎么实现工做的,它屏蔽了底层的实现细节。linux

      

  您必须为您的产品所提供的特定硬件实现相应的 HAL(和驱动程序)。HAL 实现一般会内置在共享库模块(.so 文件)中,但 Android 并不要求 HAL 实现与设备驱动程序之间进行标准交互,所以您能够视状况采起适当的作法。不过,要使 Android 系统可以与您的硬件正确互动,您必须遵照各个特定于硬件的 HAL 接口中定义的合同。android

  为了保证 HAL 具备可预测的结构,每一个特定于硬件的 HAL 接口都要具备 hardware/libhardware/include/hardware/hardware.h 中定义的属性。这类接口可以让 Android 系统以一致的方式加载 HAL 模块的正确版本。HAL 接口包含两个组件:模块和设备。express

1.1 HAL 模块

  模块表示被封装且存储为共享库 (.so file) 的 HAL 实现。hardware/libhardware/include/hardware/hardware.h标头文件会定义一个表示模块的结构体 (hw_module_t),其中包含模块的版本、名称和做者等元数据。Android 会根据这些元数据来找到并正确加载 HAL 模块。apache

/** * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM * and the fields of this data structure must begin with hw_module_t * followed by module specific information. */ typedef struct hw_module_t { /** tag must be initialized to HARDWARE_MODULE_TAG */ uint32_t tag; //tag,根据引文注释能够看到必须被初始化为HARDWARE_MODULE_TAG

    /** major version number for the module */ uint16_t version_major;//主版本号

    /** minor version number of the module */ uint16_t version_minor;//次版本号

    /** Identifier of module */
    const char *id;//模块id字符串

    /** Name of this module */
    const char *name;//模块名

    /** Author/owner/implementor of the module */
    const char *author;//做者

    /** Modules methods */
    struct hw_module_methods_t* methods;//硬件模块方法结构体

    /** module's dso */
    void* dso;//打开硬件模块的库时获得的句柄

    /** padding to 128 bytes, reserved for future use */ uint32_t reserved[32-7]; } hw_module_t;

  另外,hw_module_t 结构体还包含指向另外一个结构体 hw_module_methods_t 的指针,后面这个结构体会包含一个指向相应模块的 open 函数的指针。此 open 函数用于与相关硬件(此 HAL 是其抽象形式)创建通讯。api

typedef struct hw_module_methods_t { /** Open a specific device */
    int (*open)(const struct hw_module_t* module, const char* id,//打开硬件设备函数指针
            struct hw_device_t** device); } hw_module_methods_t;

  hw_device_t,这个结构体主要是用来描述模块中硬件设备的属性信息什么的。一个硬件模块可能有多个硬件设备。数组

  好比说,传感器模块,sensor_module,是一个硬件模块,可是手机中的传感器就对应的有好多种,好比加速度acc_sensor,磁传感器M_sensor等,那么他们都属于sensor_module,可是他们有都有本身的hw_device_t结构体来描述。hw_device_t定义:架构

/** * Every device data structure must begin with hw_device_t * followed by module specific public methods and attributes. */ typedef struct hw_device_t { /** tag must be initialized to HARDWARE_DEVICE_TAG */ uint32_t tag; //设备tag

    /** version number for hw_device_t */ uint32_t version;//版本

    /** reference to the module this device belongs to */
    struct hw_module_t* module;//本设备归属的硬件模块

    /** padding reserved for future use */ uint32_t reserved[12];//保留

    /** Close this device */
    int (*close)(struct hw_device_t* device);//关闭设备的函数指针
 } hw_device_t;

  第三个成员module指向的是这个设备归属的硬件模块结构体。app

  每一个特定于硬件的 HAL 一般都会使用附加信息为该特定硬件扩展通用的 hw_module_t 结构体。例如,在相机 HAL 中,camera_module_t 结构体会包含一个 hw_module_t 结构体以及其余特定于相机的函数指针:框架

typedef struct camera_module { hw_module_t common; int (*get_number_of_cameras)(void); int (*get_camera_info)(int camera_id, struct camera_info *info); } camera_module_t;

  实现 HAL 并建立模块结构体时,您必须将其命名为 HAL_MODULE_INFO_SYM。如下是 Nexus 9 音频 HAL 的示例:

struct audio_module HAL_MODULE_INFO_SYM = { .common = { .tag = HARDWARE_MODULE_TAG, .module_api_version = AUDIO_MODULE_API_VERSION_0_1, .hal_api_version = HARDWARE_HAL_API_VERSION, .id = AUDIO_HARDWARE_MODULE_ID, .name = "NVIDIA Tegra Audio HAL", .author = "The Android Open Source Project", .methods = &hal_module_methods, }, };

1.2 HAL 设备

  设备是产品硬件的抽象表示。例如,一个音频模块可能包含主音频设备、USB 音频设备或蓝牙 A2DP 音频设备。

  设备由 hw_device_t 结构体表示。与模块相似,每类设备都定义了一个通用 hw_device_t 的详细版本,其中包含指向硬件特定功能的函数指针。例如,audio_hw_device_t 结构体类型会包含指向音频设备操做的函数指针:

struct audio_hw_device { struct hw_device_t common; /** * used by audio flinger to enumerate what devices are supported by * each audio_hw_device implementation. * * Return value is a bitmask of 1 or more values of audio_devices_t */ uint32_t (*get_supported_devices)(const struct audio_hw_device *dev); ... }; typedef struct audio_hw_device audio_hw_device_t;

   除了这些标准属性以外,每一个特定于硬件的 HAL 接口均可以定义更多的自有功能和要求。有关详情,请参阅 HAL 参考文档以及各 HAL 的单独说明。

1.3 编译 HAL 模块

  HAL 实现会内置在模块 (.so) 文件中,并由 Android 适时地动态连接。您能够为每一个 HAL 实现建立 Android.mk 文件并指向源文件,从而编译模块。通常来讲,您的共享库必须以特定格式命名,以方便找到并正确加载。各模块的命名方案略有不一样,但它们都遵循如下通用模式:<module_type>.<device_name>

1.4 HAL 类型

  为了更好地实现模块化,Android 8.0 对 Android 操做系统底层进行了从新架构。做为此变化的一部分,运行 Android 8.0 的设备必须支持绑定式或直通式 HAL:

  • 绑定式 HAL。以 HAL 接口定义语言 (HIDL) 表示的 HAL。这些 HAL 取代了早期 Android 版本中使用的传统 HAL 和旧版 HAL。在绑定式 HAL 中,Android 框架和 HAL 之间经过 Binder 进程间通讯 (IPC) 调用进行通讯。全部在推出时即搭载了 Android 8.0 或后续版本的设备都必须只支持绑定式 HAL。
  • 直通式 HAL。以 HIDL 封装的传统 HAL 或旧版 HAL。这些 HAL 封装了现有的 HAL,可在绑定模式和 Same-Process(直通)模式下使用。升级到 Android 8.0 的设备可使用直通式 HAL。

1.4.1  HAL 模式要求

设备 直通式 绑定式
搭载 Android 8.0 的设备 直通式 HAL 中列出的 HAL 必须为直通式。 全部其余 HAL 均为绑定式(包括做为供应商扩展程序的 HAL)。
升级到 Android 8.0 的设备 直通式 HAL 中列出的 HAL 必须为直通式。 绑定式 HAL 中列出的 HAL 必须为绑定式。
供应商映像提供的全部其余 HAL 既能够在直通模式下使用,也能够在绑定模式下使用。

 

1.4.2 绑定式 HAL

Android 要求全部 Android 设备(不管是搭载 Android O 的设备仍是升级到 Android O 的设备)上的下列 HAL 均为绑定式:

  • android.hardware.biometrics.fingerprint@2.1。取代 Android 8.0 中已不存在的 fingerprintd
  • android.hardware.configstore@1.0。Android 8.0 中的新 HAL。
  • android.hardware.dumpstate@1.0。此 HAL 提供的原始接口可能没法继续使用,而且已更改。所以,dumpstate_board 必须在指定的设备上从新实现(这是一个可选的 HAL)。
  • android.hardware.graphics.allocator@2.0。在 Android 8.0 中,此 HAL 必须为绑定式,所以无需在可信进程和不可信进程之间分享文件描述符。
  • android.hardware.radio@1.0。取代由存活于自身进程中的 rild 提供的接口。
  • android.hardware.usb@1.0。Android 8.0 中的新 HAL。
  • android.hardware.wifi@1.0。Android 8.0 中的新 HAL,可取代此前加载到 system_server 的旧版 WLAN HAL 库。
  • android.hardware.wifi.supplicant@1.0。在现有 wpa_supplicant 进程之上的 HIDL 接口

注意:Android 提供的如下 HIDL 接口将一概在绑定模式下使用:android.frameworks.*android.system.* 和 android.hidl.*(不包括下文所述的 android.hidl.memory@1.0)。

1.4.3 直通式 HAL

Android 要求全部 Android 设备(不管是搭载 Android O 的设备仍是升级到 Android O 的设备)上的下列 HAL 均在直通模式下使用:

  • android.hardware.graphics.mapper@1.0。将内存映射到其所属的进程中。
  • android.hardware.renderscript@1.0。在同一进程中传递项(等同于 openGL)。

上方未列出的全部 HAL 在搭载 Android O 的设备上都必须为绑定式。

Same-Process HAL

Same-Process HAL (SP-HAL) 一概在使用它们的进程中打开,其中包括未以 HIDL 表示的全部 HAL,以及那些非绑定式的 HAL。SP-HAL 集的成员只能由 Google 控制,这一点没有例外。

SP-HAL 包括如下 HAL:

  • openGL
  • Vulkan
  • android.hidl.memory@1.0(由 Android 系统提供,一概为直通式)
  • android.hardware.graphics.mapper@1.0
  • android.hardware.renderscript@1.0

1.5 传统 HAL 和旧版 HAL

  传统 HAL(在 Android 8.0 中已弃用)是指与具备特定名称及版本号的应用二进制接口 (ABI) 标准相符的接口。大部分 Android 系统接口(相机音频传感器等)都采用传统 HAL 形式(已在 hardware/libhardware/include/hardware 下进行定义)。

  旧版 HAL(也已在 Android 8.0 中弃用)是指早于传统 HAL 的接口。一些重要的子系统(WLAN、无线接口层和蓝牙)采用的就是旧版 HAL。虽然没有统一或标准化的方式来指明是否为旧版 HAL,但若是 HAL 早于 Android 8.0 而出现,那么这种 HAL 若是不是传统 HAL,就是旧版 HAL。有些旧版 HAL 的一部分包含在 libhardware_legacy 中,而其余部分则分散在整个代码库中。

2、操做硬件

  HAL层的主要的两个结构体hw_module_t(硬件模块)和hw_device_t(硬件设备)的成员,下面咱们来具体看看上层app究竟是怎么实现操做硬件的?

  一些硬件厂商不肯意将本身的一些核心代码开放出去,因此将这些代码放到HAL层,可是怎么保证它不开放呢?HAL层代码不是也让你们知道下载吗?其实硬件厂商的HAL核心代码是以共享库的形式出现的,每次在须要的时候,hal会自动加载调用相关共享库。那么是怎么加载找到某一硬件设备对应的共享库的呢?这也是咱们这篇都要说的。

   上层app经过jni调用hal层的hw_get_module函数获取硬件模块,这个函数是上层与hal打交道的入口。因此若是咱们以程序调用执行的流程去看源码的话,这个函数就是hal层第一个被调用的函数,下面咱们就从这个函数开始,沿着程序执行的流程走下去。

  hw_get_module函数定义在/hardware/libhardware/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 */
#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 "<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])); /** * 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 = -EINVAL; void *handle = NULL; struct hw_module_t *hmi = NULL; /* * 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 = 0; char prop[PATH_MAX] = {0}; char path[PATH_MAX] = {0}; char name[PATH_MAX] = {0}; char prop_name[PATH_MAX] = {0}; 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); }

  在dlopen的()函数以指定模式打开指定的动态链接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。

hw_get_module
 
 
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;//检查system路径是否有库文件
 snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH2, id, prop); if (access(path, R_OK) == 0) break;//检查vender路径是否有库文件
        } 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);//装载库,获得module
 } return status; }

  snprintf(),函数原型为int snprintf(char *str, size_t size, const char *format, ...)。将可变参数 “…” 按照format的格式格式化为字符串,而后再将其拷贝至str中。

access(filename, 0) 表示判断文件是否存在 access 返回值是0的时候,表示存在,而返回-1的时候,表示失败。

  property_set/property_get位于libcutils.so库。任何进程若要调用这两个函数,须要连接libcutils.so(https://blog.csdn.net/decisiveness/article/details/49852295)。

   看第一行咱们知道有两个参数,第一参数id就是要获取的硬件模块的id,第二个参数module就是咱们想获得的硬件模块结构体的指针。因此能够看出,上层首先给hal须要获取的硬件模块的id,hw_get_module函数根据这个id去查找匹配和这个id对应的硬件模块结构体的。

  下面看看怎么找的:

  17行有个for循环,上限是HAL_VARIANT_KEYS_COUNT+1,那么这个HAL_VARIANT_KEYS_COUNT是什么呢?查看同文件下找到有:

static const int HAL_VARIANT_KEYS_COUNT = (sizeof(variant_keys)/sizeof(variant_keys[0]));

  原来它是ariant_keys这个数组的元素个数。那么这个数组又是什么呢?在本文件找,有:

/** * 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" };

  能够看到它实际上是个字符串数组。暂且不知道干什么的。继续看hw_get_module函数,进入for循环里面,看22行,其实它是将HAL_LIBRARY_PATH1, id, prop这三个串拼凑一个路径出来,

HAL_LIBRARY_PATH1定义以下:

/** Base path of the hal modules */
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"

  id是上层提供的,prop这个变量的值是前面19行property_get(variant_keys[i], prop, NULL)函数获取到的,其实这个函数是经过ariant_keys数组的的属性查找到系统中对应的变种名称。不一样的平台获取到prop值是不同的。

  假如在获取到的prop值是tout,须要获取的硬件模块的id是leds,那么最后path组成的串是/system/lib/hw/leds.tout.so。后面24行access是检查这个路径下是否存在,若是有就break,跳出循环。若是没有,继续走下面,

  能够看到下面几行和刚才形式差很少,

snprintf(path, sizeof(path), "%s/%s.%s.so",  HAL_LIBRARY_PATH2, id, prop);

if (access(path, R_OK) == 0) break;//检查vender路径是否有库文件

  结合 HAL_LIBRARY_PATH2 为"/vendor/lib/hw",假设一样获取到的prop值是tout,须要获取的硬件模块的id是leds,这种状况下path拼出来的值是/vender/lib/hw/leds.tout.so,而后在判断文件是否存在。若是存在跳出循环。

  从以上分析,其实这就是hal层搜索动态共享库的方式,从中咱们能够获得两点:

  • 1.动态共享库通常放在 "/system/lib/hw"和"/vendor/lib/hw"这两个路径下。
  • 2.动态库的名称是以"id.variant.so"的形式命名的,其中id为上层提供,中间variant为变种名称,是随系统平台变化的。

接着,从29到32行咱们能够看到,当全部变种名称形式的包都不存在时,就以"id.default.so"形式包名查找是否存在。

37行, if (i < HAL_VARIANT_KEYS_COUNT+1),若是i小于变种名称数组的话,表示找到了对应的库,那么38行load(id, path, module);//装载库,获得module。

三 如何实现加载共享库

  如下为load函数定义,一样在/hardware/libhardware/hardware.c中实现的。

/** * 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) {//传入硬件模块id和库所在路径,获取到硬件模块结构体
    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) {//匹配解析出硬件模块的id和传入咱们实际想要获得的模块id是否一致
        LOGE("load: id=%s != hmi->id=%s", id, hmi->id); status = -EINVAL; goto done; } hmi->dso = handle;  //将打开库获得句柄传给硬件模块的dso

    /* 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;//将获得的module的结果经过第三个参数传给hw_module_t

    return status; }

Linux提供了一套API来动态装载库。下面列出了这些API:

- dlopen,打开一个库,并为使用该库作些准备。
- dlsym,在打开的库中查找符号的值。
- dlclose,关闭库。
- dlerror,返回一个描述最后一次调用dlopen、dlsym,或dlclose的错误信息的字符串。

C语言用户须要包含头文件dlfcn.h才能使用上述API。glibc还增长了两个POSIX标准中没有的API:
- dladdr,从函数指针解析符号名称和所在的文件。
- dlvsym,与dlsym相似,只是多了一个版本字符串参数。

  能够看到load函数传入的几个参数:

  第一个参数就是须要加载的硬件模块对应动态库的硬件模块的id,

  第二个参数就是动态库存放的路径,就是在hw_get_module函数前部分搜索库获得的path,

  第三个参数就是咱们须要获得的硬件模块结构体,经过它传给hw_get_module,hw_get_module函数在经过参数传给jni。

  第19行,首先调用dlopen打开共享库,该函数经过传入的库的路径找到库,而且打开它,传回一个操做句柄handle,而后再调用dlsym函数解析这个打开的库,下面第29行,获得库中包含的硬件模块结构体,并将它返回回来。因此硬件厂商或者硬件移植者都必须根据hal的这个架构去实现填充这个和本身硬件相关的硬件模块结构体hw_module_t,供使用。

  经过dlsym解析以后就获得了hw_module_t,随后第37行,将从库中解析获得的结构体中的id和传入的id作比较,看是否一致。若是一致则证实就是获得正确的硬件模块了。最后第60行,将hw_module_t结构体指针传给第三个参数,传给hw_get_module函数。

  到此,hw_get_module函数就获得了硬件模块结构体hw_module_t.有了hw_module_t,那么经过其内部的method open就能打开硬件模块对应的设备了,经过结构体中的一些方法就能操做硬件设备了。

四 实践

见番外篇

参考:

https://www.cnblogs.com/y041039/archive/2013/05/22/3092774.html

https://www.cnblogs.com/y041039/archive/2013/05/22/3092868.html

https://www.cnblogs.com/y041039/archive/2013/05/23/3094579.html

https://www.cnblogs.com/y041039/archive/2013/05/23/3094928.html

https://www.cnblogs.com/y041039/archive/2013/05/23/3095621.html

https://www.linuxidc.com/Linux/2011-07/38983.htm

进一步了解  Linux内核 -> HAL -> JNI -> framework -> app:

别人的实例

 

Android硬件抽象层(HAL)概要介绍和学习计划

 

Android之 看“马达”如何贯通Android系统 (从硬件设计 --> 驱动 --> HAL --> JNI --> Framework --> Application)

Android应用层到Framework到HAL再到驱动层的整个流程分析

相关文章
相关标签/搜索