Android的HAL是为了保护一些硬件提供商的知识产权而提出的,是为了避开Linux的GPL束缚。思路是把控制硬件的动作都放到了Android HAL中,而linux driver仅仅完成一些简单的数据交互作用,甚至把硬件寄存器空间直接映射到user space。而Android是基于Aparch的license,因此硬件厂商可以只提供二进制代码,所以说Android只是一个开放的平台,并不是一个开源的平台。
Android的硬件抽象层,简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,而Linux内核驱动程序运行在内核空间。为什么要这样安排呢?把硬件抽象层和内核驱动整合在一起放在内核空间不可行吗?从技术实现的角度来看,是可以的,然而从商业的角度来看,把对硬件的支持逻辑都放在内核空间,可能会损害厂家的利益。我们知道,Linux内核源代码版权遵循GNU License,而Android源代码版权遵循Apache License,前者在发布产品时,必须公布源代码,而后者无须发布源代码。如果把对硬件支持的所有代码都放在Linux驱动层,那就意味着发布时要公开驱动程序的源代码,而公开源代码就意味着把硬件的相关参数和实现都公开了,在手机市场竞争激烈的今天,这对厂家来说,损害是非常大的。因此,Android才会想到把对硬件的支持分成硬件抽象层和内核驱动层,内核驱动层只提供简单的访问硬件逻辑,例如读写硬件寄存器的通道,至于从硬件中读到了什么值或者写了什么值到硬件中的逻辑,都放在硬件抽象层中去了,这样就可以把商业秘密隐藏起来了。也正是由于这个分层的原因,Android被踢出了Linux内核主线代码树中。大家想想,Android放在内核空间的驱动程序对硬件的支持是不完整的,把Linux内核移植到别的机器上去时,由于缺乏硬件抽象层的支持,硬件就完全不能用了,这也是为什么说Android是开放系统而不是开源系统的原因。
撇开这些争论,学习Android硬件抽象层,对理解整个Android整个系统,都是极其有用的,因为它从下到上涉及到了Android系统的硬件驱动层、硬件抽象层、运行时库和应用程序框架层等等,下面这个图阐述了硬件抽象层在Android系统中的位置,以及它和其它层的关系:
在学习Android硬件抽象层的过程中,我们将会学习如何在内核空间编写硬件驱动程序、如何在硬件抽象层中添加接口支持访问硬件、如何在系统启动时提供硬件访问服务以及 如何编写JNI使得可以通过Java接口来访问硬件,而作为中间的一个小插曲,我们还将学习一下如何在Android系统中添加一个C可执行程序来访问硬件驱动程序。
一、代码文件介绍
/hardware/libhardware_legacy/ - 旧的架构、采取链接库模块的方式
/hardware/libhardware 新架构、调整为 HAL stub 目录的结构如下:
/hardware/libhardware/hardware.c 编译成libhardware.s置于/system/lib
/hardware/libhardware/include/hardware目录下包含如下头文件:
hardware.h 通用硬件模块头文件
copybit.h copybit模块头文件
gralloc.h gralloc模块头文件
lights.h 背光模块头文件
overlay.h overlay模块头文件
qemud.h qemud模块头文件
sensors.h 传感器模块头文件
/hardware/libhardware/modules 目录下定义了很多硬件模块
/hardware/msm7k /hardware/qcom /hardware/ti /device/Samsung
/device/moto 各个厂商平台相关的hal
这些硬件模块都编译成xxx.xxx.so,目标位置为/system/lib/hw目录
二、HAL层实现方式
目前HAL存在两种构架,位于libhardware_legacy目录下的“旧HAL架构”和位于libhardware目录下的“新HAL架构”。两种框架如下图所示:
libhardware_legacy 是将 *.so 文件当作shared library来使用,在runtime(JNI 部份)以 direct function call 使用 HAL module。通过直接函数调用的方式,来操作驱动程序。当然,应用程序也可以不需要通过 JNI 的方式进行,直接加载 *.so (dlopen)的做法调用*.so 里的符号(symbol)也是一种方式。总而言之是没有经过封装,上层可以直接操作硬件。
现在的libhardware 架构,就有stub的味道了。HAL stub 是一种代理人(proxy)的概念,stub 虽然仍是以 *.so檔的形式存在,但HAL已经将 *.so 档隐藏起来了。Stub 向 HAL提供操作函数(operations),而 runtime 则是向 HAL 取得特定模块(stub)的 operations,再 callback 这些操作函数。这种以 indirect function call 的架构,让HAL stub 变成是一种包含关系,即 HAL 里包含了许许多多的 stub(代理人)。Runtime 只要说明类型,即 module ID,就可以取得操作函数。对于目前的HAL,可以认为Android定义了HAL层结构框架,通过几个接口访问硬件从而统一了调用方式。
Android的HAL的实现需要通过JNI(JavaNative Interface),JNI简单来说就是Java程序可以调用C/C++写的动态链接库,这样的话,HAL可以使用C/C++语言编写,效率更高。JNI->通用硬件模块->硬件模块->内核驱动接口,具体一点:JNI->libhardware.so->xxx.xxx.so->kernel,具体来说:android frameworks中JNI调用hardware.c中定义的hw_get_module函数来获取硬件模块,然后调用硬件模块中的方法,硬件模块中的方法直接调用内核接口完成相关功能
在Android下访问HAL大致有以下两种方式:
(1)Android的app可以直接通过service调用.so格式的jni
(2)经过Manager调用service
上面两种方法应该说是各有优缺点,第一种方法简单高效,但不正规。第二种方法实现起来比较复杂,但更符合目前的Android框架。第二种方法中,LegManager和LedService(java)在两个进程中,需要通过进程通讯的方式来通讯。
在现在的android框架中,这两种方式都存在,比如对于lights,是直接透过LightsService调用JNI,而对于sensor,中间则是通过SensorsManager
来调用JNI的。
三、源码分析
最近在看SurfaceFlinger,我们就以这个为切入点,我们来看下面这段代码,通过hw_get_module函数通过HAL层获取到module,然后通过gralloc_open函数通过module来获取device。
- GraphicBufferAllocator::GraphicBufferAllocator()
- : mAllocDev(0)
- {
- hw_module_t const* module;
- int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
- if (err == 0) {
- gralloc_open(module, &mAllocDev);
- }
- }
3.1 hw_get_modeule
我们先来看hardware/libhardware/hardware.c中的hw_get_module函数,调用了hw_get_module_by_class函数。
- int hw_get_module(const char *id, const struct hw_module_t **module)
- {
- return hw_get_module_by_class(id, NULL, module);
- }
我们配合上面的例子来分析hw_get_module函数,在GraphicBufferAllocator构造函数中,调用的hw_get_module函数,参数GRALLOC_HARDWARE_MODULE_ID我们看看是什么值
在hardware/libhardware/include/hardware/gralloc.h中定义了该宏
- #define GRALLOC_HARDWARE_MODULE_ID "gralloc"
再来看下hw_get_module_by_class函数
- 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);//这个时候name就是gralloc
- snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);//先获取ro.hardware.gralloc这个属性值
- 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++) {//从variant_keys数组中获取其属性值,然后看能否找到其动态库
- 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) {//最后还没找到直接用default传入
- 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);
- }
我们再来看看hw_module_exists函数,用来看动态库是否存在,其中用到了HAL_LIBRARY_PATH1和HAL_LIBRARY_PATH1两个宏。然后将传进来的参数组合起来加上后缀名so,看看是否存在这样的so动态库。
- 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;
- }
下面我们看下两个宏
- #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
我们再来看variant_keys数组中,用来获取属性的值,这个属性值是一个中间值。比如上面的例子应该是这样的"gralloc.属性值.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"
- };
最后如果还没找到,我们就用default,比如"gralloc.default.so"
最后找到了我们用load来加载,返回一个hw_modult_t类型的module
- 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;
- }
3.2 gralloc模块hal层实现
我们先来看看gralloc_module_t 这个结构体,先看下英文解释。每个硬件模块必须有一个数据结构name是HAL_MODULE_INFO_SYM,而且其中必须有一个成员变量是hw_modult_t类型的,而且必须是第一位。下面这个定义也是在gralloc.h中
- /**
- * 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 gralloc_module_t {
- struct hw_module_t common;
- int (*unregisterBuffer)(struct gralloc_module_t const* module,
- buffer_handle_t handle);
- int (*lock)(struct gralloc_module_t const* module,
- buffer_handle_t handle, int usage,
- int l, int t, int w, int h,
- void** vaddr);
- int (*unlock)(struct gralloc_module_t const* module,
- buffer_handle_t handle);
- /* reserved for future use */
- int (*perform)(struct gralloc_module_t const* module,
- int operation, ... );
- int (*lock_ycbcr)(struct gralloc_module_t const* module,
- buffer_handle_t handle, int usage,
- int l, int t, int w, int h,
- struct android_ycbcr *ycbcr);
- int (*lockAsync)(struct gralloc_module_t const* module,
- buffer_handle_t handle, int usage,
- int l, int t, int w, int h,
- void** vaddr, int fenceFd);
- int (*unlockAsync)(struct gralloc_module_t const* module,
- buffer_handle_t handle, int* fenceFd);
- int (*lockAsync_ycbcr)(struct gralloc_module_t const* module,
- buffer_handle_t handle, int usage,
- int l, int t, int w, int h,
- struct android_ycbcr *ycbcr, int fenceFd);
- /* reserved for future use */
- void* reserved_proc[3];
- } gralloc_module_t;
我们再来看下hw_modult_t类型,这个定义在hardware.h中
- typedef struct hw_module_t {
- /** tag must be initialized to HARDWARE_MODULE_TAG */
- uint32_t tag;
- uint16_t module_api_version;
- #define version_major module_api_version
- #define version_minor hal_api_version
- /** Identifier of module */
- const char *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;
- #ifdef __LP64__
- uint64_t reserved[32-7];
- #else
- /** padding to 128 bytes, reserved for future use */
- uint32_t reserved[32-7];
- #endif
- } hw_module_t;
上面其中有一个methods变量类型是hw_module_methods_t我们来看下。每个模块要实现这个结构体中的open函数,最终返回hw_device_t类型的device
- 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;
我们再看gralloc模块的实现文件gralloc_modulc.cpp
- private_module_t:: ()
- {
- #define INIT_ZERO(obj) (memset(&(obj),0,sizeof((obj))))
- base.common.tag = HARDWARE_MODULE_TAG;
- base.common.version_major = 1;
- base.common.version_minor = 0;
- base.common.id = GRALLOC_HARDWARE_MODULE_ID;
- base.common.name = "Graphics Memory Allocator Module";
- base.common.author = ".......";
- base.common.methods = &gralloc_module_methods;//这个方法是关键
- base.common.dso = NULL;
- INIT_ZERO(base.common.reserved);
- base.registerBuffer = gralloc_register_buffer;
- base.unregisterBuffer = gralloc_unregister_buffer;
- base.lock = gralloc_lock;
- base.unlock = gralloc_unlock;
- base.perform = NULL;
- INIT_ZERO(base.reserved_proc);
- framebuffer = NULL;
- flags = 0;
- numBuffers = 0;
- bufferMask = 0;
- pthread_mutex_init(&(lock), NULL);
- refcount = 0;
- currentBuffer = NULL;
- INIT_ZERO(info);
- INIT_ZERO(finfo);
- xdpi = 0.0f;
- ydpi = 0.0f;
- fps = 0.0f;
- swapInterval = 1;
- initialize_blk_conf();
- ion_client = -1;
- #undef INIT_ZERO
- };
- /*
- * HAL_MODULE_INFO_SYM will be initialized using the default constructor
- * implemented above
- */
- struct private_module_t HAL_MODULE_INFO_SYM;//HAL_MODULE_INFO_SYM变量
上面实现HAL_MODULE_INFO_SYM的类型是private_modult_t,是在hardware/libhardware/modules/gralloc/gralloc_priv.h中的
- struct private_module_t {
- gralloc_module_t base;
- private_handle_t* framebuffer;
- uint32_t flags;
- uint32_t numBuffers;
- uint32_t bufferMask;
- pthread_mutex_t lock;
- buffer_handle_t currentBuffer;
- int pmem_master;
- void* pmem_master_base;
- struct fb_var_screeninfo info;
- struct fb_fix_screeninfo finfo;
- float xdpi;
- float ydpi;
- float fps;
- };
我们再来看下gralloc_module_methods变量实现了open函数。
- static struct hw_module_methods_t gralloc_module_methods =
- {
- open: gralloc_device_open
- };
然后我们再来看上面,获取了module后,又调用了gralloc_open函数
- GraphicBufferAllocator::GraphicBufferAllocator()
- : mAllocDev(0)
- {
- hw_module_t const* module;
- int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
- if (err == 0) {
- gralloc_open(module, &mAllocDev);
- }
- }
gralloc_open函数在gralloc.h中,这里调用模块methods中的open方法,前面说过最后会调用gralloc_device_open函数
- static inline int gralloc_open(const struct hw_module_t* module,
- struct alloc_device_t** device) {
- return module->methods->open(module,
- GRALLOC_HARDWARE_GPU0, (struct hw_device_t**)device);
- }
gralloc_device_open函数如下,因为我们传入的是GRALLOC_HARDWARE_GPU0,因此最后调用alloc_device_open函数来返回device。
- static int gralloc_device_open(const hw_module_t* module, const char* name, hw_device_t** device)
- {
- int status = -EINVAL;
- if (!strncmp(name, GRALLOC_HARDWARE_GPU0, MALI_GRALLOC_HARDWARE_MAX_STR_LEN))
- {
- status = alloc_device_open(module, name, device);
- }
- else if (!strncmp(name, GRALLOC_HARDWARE_FB0, MALI_GRALLOC_HARDWARE_MAX_STR_LEN))
- {
- status = framebuffer_device_open(module, name, device);
- }
- return status;
- }
我们先来看看这个函数,在alloc_device.cpp中,这里新建了一个alloc_device_t,然后将其common变量赋给了device,因为common变量是alloc_device_t中的第一个变量,它的地址也就是alloc_device_t的地址。
- int alloc_device_open(hw_module_t const* module, const char* name, hw_device_t** device)
- {
- alloc_device_t *dev;
- dev = new alloc_device_t;
- if (NULL == dev)
- {
- return -1;
- }
- #if USE_VIVANTE_2D == 1
- if (has2Ddev == -1)
- {
- struct stat buf;
- if (stat("/dev/graphics/galcore_smmu", &buf) == 0)
- {
- ALOGI("%s: galcore and smmu both exist!", __FUNCTION__);
- has2Ddev = 1;
- SMMUEnable = 1;
- }
- else if (stat("/dev/graphics/galcore", &buf) == 0)
- {
- ALOGI("%s: only galcore exist!", __FUNCTION__);
- has2Ddev = 1;
- SMMUEnable = 0;
- }
- else
- {
- ALOGI("%s: check galcore failed", __FUNCTION__);
- has2Ddev = 0;
- SMMUEnable = 0;
- }
- }
- #endif
- /* initialize our state here */
- memset(dev, 0, sizeof(*dev));
- /* initialize the procs */
- dev->common.tag = HARDWARE_DEVICE_TAG;
- dev->common.version = 0;
- dev->common.module = const_cast<hw_module_t*>(module);
- dev->common.close = alloc_backend_close;
- dev->alloc = alloc_device_alloc;
- dev->free = alloc_device_free;
- if (0 != alloc_backend_open(dev)) {
- delete dev;
- return -1;
- }
- *device = &dev->common;//common变量是第一个把 它的地址也就是alloc_device_t的地址
- return 0;
- }
我们来看下alloc_device_t结构体在gralloc.h中,其第一个变量是hw_device_t
- typedef struct alloc_device_t {
- struct hw_device_t common;
- int (*alloc)(struct alloc_device_t* dev,
- int w, int h, int format, int usage,
- buffer_handle_t* handle, int* stride);
- int (*free)(struct alloc_device_t* dev,
- buffer_handle_t handle);
- void (*dump)(struct alloc_device_t *dev, char *buff, int buff_len);
- void* reserved_proc[7];
- } alloc_device_t;
我们再来看看hw_device_t结构体,在hardware.h文件中,我们看起英文注释,hw_device_t必须在上面alloc_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 {
- uint32_t tag;
- uint32_t version;
- /** reference to the module this device belongs to */
- struct hw_module_t* module;
- /** padding reserved for future use */
- #ifdef __LP64__
- uint64_t reserved[12];
- #else
- uint32_t reserved[12];
- #endif
- /** Close this device */
- int (*close)(struct hw_device_t* device);
- } hw_device_t;
- GraphicBufferAllocator::GraphicBufferAllocator()
- : mAllocDev(0)
- {
- hw_module_t const* module;
- int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
- if (err == 0) {
- gralloc_open(module, &mAllocDev);
- }
- }
四、注意
这里有几点注意点,我们来看下
4.1 JNI层方法调用
我们有几点要注意,一是上层代码可以如下。
- GraphicBufferAllocator::GraphicBufferAllocator()
- : mAllocDev(0)
- {
- hw_module_t const* module;
- int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
- if (err == 0) {
- gralloc_open(module, &mAllocDev);
- }
- }
也可以这样,这样都是可以的。
- GraphicBufferAllocator::GraphicBufferAllocator()
- : mAllocDev(0)
- {
- private_module_t const* module;
- int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
- if (err == 0) {
- gralloc_open(module->base->common, &mAllocDev);
- }
- }
为什么呢?因为,不管你中间有数据结构,但是因为都在第一个变量,其指针地址都是一致的。到时候想用什么类型。只要指针类型转化下就可以了。还可以这样,都是没有问题的。
- GraphicBufferAllocator::GraphicBufferAllocator()
- : mAllocDev(0)
- {
- gralloc_module_t const* module;
- int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
- if (err == 0) {
- gralloc_open(module->common, &mAllocDev);
- }
- }
4.2 Android.mk文件
Android.mk我们要注意什么呢?下面是上面gralloc模块的Android.mk文件。首先我们编译的是动态库这个没有问题,其次我们目录前面我们分析过,名字在vendor/lib/hw或者system/lib/hw下。最后我们的模块名字,要和前面分析的几个属性名字要符合
- ......
- LOCAL_MODULE_RELATIVE_PATH := hw
- ......
- LOCAL_MODULE := gralloc.$(TARGET_BOARD_PLATFORM)
- ......
- include $(BUILD_SHARED_LIBRARY)
- static const char *variant_keys[] = {
- "ro.hardware",
- "ro.product.board",
- "ro.board.platform",
- "ro.arch"
- };
这里我们是系统模块,那比如我们自己写个hal层模块,应该用什么呢?模块名后面有default,就是上面分析的如果都找不到会用default。PATH这里没有使用相对路径,直接用的绝对路径,一个意思。
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE_TAGS := optional
- LOCAL_PRELINK_MODULE := false
- LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
- LOCAL_SHARED_LIBRARIES := liblog
- LOCAL_SRC_FILES := hello.c
- LOCAL_MODULE := hello.default
- include $(BUILD_SHARED_LIBRARY)