一 什么是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之 看“马达”如何贯通Android系统 (从硬件设计 --> 驱动 --> HAL --> JNI --> Framework --> Application)