Android的HAL是为了保护一些硬件提供商的知识产权而提出的,是为了避开linux的GPL束缚。思路是把控制硬件的动作都放到了Android HAL中,而linux driver仅仅完成一些简单的数据交互作用,甚至把硬件寄存器空间直接映射到user space。而Android是基于Aparch的license,因此硬件厂商可以只提供二进制代码,所以说Android只是一个开放的平台,并不是一个开源的平台。

一、代码文件介绍

/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(Java Native 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);elsestrlcpy(name, class_id, PATH_MAX);//这个时候name就是grallocsnprintf(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 differentfile 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 == 1if (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];
#elseuint32_t reserved[12];
#endif/** Close this device */int (*close)(struct hw_device_t* device);} hw_device_t;

上面获取到的device是mAllocDev,类型是alloc_device_t。最后可以直接使用mAllocDev来调用比如alloc等函数。

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)

这里我们用的属性名字是ro.board.platform

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)


Android HAL层分析 (gralloc显示模块 举例)相关推荐

  1. Android Hal层简要分析

    Android Hal层简要分析 Android Hal层(即 Hardware Abstraction Layer)是Google开发的Android系统里上层应用对底层硬件操作屏蔽的一个软件层次, ...

  2. Android系统的HAL层分析 -- Sensors

    Android系统HAL层分析 -- Sensors 0 前言 1 HAL层Sensors代码分析/注释 0 前言 1 HAL层Sensors代码分析/注释

  3. Android 系统(4)---Android HAL层与Linux Kernel层驱动开发简介

    Android HAL层与Linux Kernel层驱动开发简介 近日稍微对Android中的驱动开发做了一些简要的了解,稍稍理清了一下Android驱动开发的套路,总结一下笔记. HAL:Hardw ...

  4. Android HAL层与Linux Kernel层驱动开发简介

    Android HAL层与Linux Kernel层驱动开发简介 阅读数:5070 近日稍微对Android中的驱动开发做了一些简要的了解,稍稍理清了一下Android驱动开发的套路,总结一下笔记. ...

  5. Android HAL 层,三个重要的结构体的源码~

    最近在学习android HAL 层 ~ 离不开这三个数据结构~~先贴出来 typedef struct hw_module_t {     /** tag must be initialized t ...

  6. Android hal 层 C 堆栈打印方法

    Android hal 层 C 堆栈打印方法 一.添加CallStack文件 在hardware/qcom/audio/hal/ 下添加 callstack.cpp 和 callstack.h文件 # ...

  7. 【转】android hal 层GPS 研究总结——关于GPS GGA/GSV/RMC 数据的解析

    [转]android hal 层GPS 研究总结 2011-09-22 快跑的未必能赢,力战的未必获胜,聪明的未必得粮食,明哲的未必得资财,灵活的未必得喜悦.所临到世人的,是在乎当时的机会. 人生一世 ...

  8. Android HAL层浅析

    文章目录 1.HAL层在Android系统中的位置 2.HAL层概述 3.旧的HAL架构module 4.新的HAL架构module stub 5.HAL Stub框架分析 1.HAL层在Androi ...

  9. Android HAL层/native C程序打印栈信息方法

    在调试Android系统底层函数时,经常需要跟踪函数调用流程,特别在HAL层需要确定参数来源时.使用栈信息逆向跟踪可快速分析函数调用流程,结合使用addr2line工具.绘图工具可绘制函数关系图.本文 ...

最新文章

  1. 根据已有项目jar文件生成maven的pom.xml
  2. sublime 设置自动更新_不止是自动更新!简单几步让Windows 10变听话的孩子
  3. 计算机汉字救亡运动简史丨视频
  4. Visual Studio 2017开发linux程序之libevent使用实例
  5. 笔记 - Ali Cloud 块存储简介
  6. Ubuntu14下搭建Web服务器
  7. aix 的c库为什么都是静态库_关于AIX  libpthread.a 静态库
  8. 读入一组文本行,打印最长的文本行
  9. 在MFC中添加用户自定义消息
  10. php基础教程 第四步 学习运算符
  11. cpu序列号唯一吗_怎么看电脑硬件是不是新的 有什么软件能检测吗?
  12. python tkinter库 密码实时显示_Python3 tkinter基础 Entry show textvariable 密码输入框
  13. python新线程打开cmd_Python – 函数无法在新线程中运行
  14. Android Room 之存储 Objects 中的 List
  15. 服务器ftp网站怎么临时关闭,如何停止pyftpdlib FTP服务器?
  16. 多台服务之间共享Session
  17. 【 理想的机器学习书】
  18. 在微信小程序开发中遇见的bug及解决方案
  19. 期货怎么克服频繁止损(期货交易怎么止损)
  20. DMAIC代表了六西格玛改进活动的五个阶段

热门文章

  1. HTML做一个简单漂亮的旅游网页(纯html代码)重庆旅游 7页
  2. xp无法访问win7计算机提示无权限,ghost xp访问win7共享无权限怎么解决
  3. 2022年嵌入式开发就业前景怎么样?
  4. 华为HCNA之SNMP基础配置实验
  5. 特别舍得花钱的穷人们
  6. 种植福田{培植福报}~福慧双修——悟透自己!
  7. Python学习笔记——字典
  8. 吴恩达机器学习第十周测试
  9. iOS 10 is the maximum deployment target for 32-bit targets
  10. vue插件开发、文档书写、github发布、npm包发布一波流