Android系统HAL层原理及编程注意事项
来源:互联网 发布:什么叫网络故障诊断 编辑:程序博客网 时间:2024/06/05 14:13
可以说,jni层,也就是虚拟机层,起到了承上启下的作用,非常重要。说啥呢,不是说HAL层吗,对,说HAL,可以说,没有HAL层,android系统照样可以搭建起来,直接通过系统调用访问驱动就好了。
二,HAL实现原理
HAL的实现原理有人总结为321条款,即3个结构体2个宏定义1个方法,的确很简单,网上一搜一大把的介绍,我就不重复了,主要就是看一下这2个文件:
android/hardware/libhardware/include/hardware/hardware.h
android/hardware/libhardware/hardware.c
其中,最重要的就是使用dlopen/dlsym进行HMI的映射,所以,没有dl库,HAL层是玩不转的。
三,HAL重要问题研究
先看一下hw_module_methods_t 中的open类型:
typedef struct hw_module_methods_t {
int (*open)(const struct hw_module_t* module, const char* id, struct hw_device_t** device);
} hw_module_methods_t;
open函数指针的参数类型是固定的,必须如此!(其实如果不是这样还能是什么样呢,HAL又不是神仙,怎么会知道你自定义的结构体类型是什么呢)。这个基本原则导致了HAL层编程有一个潜规则,这个潜规则不得不写一下,就是我们在写HAL程序时,自定义的结构体中会包括hw_module_t或者hw_device_t,潜规则规定hw_module_t或hw_device_t必须是我们自定义结构体的第一个成员变量,这是强制性的,没得商量,否则HAL层也玩不转了,详细情况看下文:
这是我在工作中的HAL层自定义的结构体:
"device/actions/common/hardware/include/display.h"
161 struct owldisp_device_t {
163 struct hw_device_t common; //必须是第一个
167 int (*get_disp_num)(struct owldisp_device_t *dev);
169 int (*get_disp_info)(struct owldisp_device_t *dev,int disp,int * info );
185 int (*set_hdmi_size)(struct owldisp_device_t *dev, int xres , int yres);
191 int (*get_hdmi_fitscreen)(struct owldisp_device_t *dev);
193 };
这是在JNI中访问HAL层的代码:
"./device/actions/common/frameworks/services/jni/com_actions_server_DisplayService.cpp"
static struct owldisp_device_t * mDisplayManager = NULL;
static jboolean actions_server_DisplayService_init(JNIEnv *env, jclass clazz) {
owldisp_module_t * module;
if (hw_get_module(DM_HARDWARE_MODULE_ID, (const hw_module_t**) &module) == 0) {
module->methods->open(module, DM_HARDWARE_MODULE_ID, (struct hw_device_t**) &mDisplayManager);
}
}
上面说了,open的第一个参数必须是struct hw_module_t,第三个参数必须是struct hw_device_t,而参数module是owldisp_module_t类型,mDisplayManager是struct owldisp_device_t类型,很明显二者都不符合参数要求,于是只能做强制类型转换,那神奇的根源就是强制类型转换这一点,继续往下:
上面module->methods->open()其实调用的是这个open_display_manager函数:
"./device/actions/common/hardware/libdisplay/display.cpp"
39 struct disp_manager_context_t {
41 struct owldisp_device_t device;
43 int mDispFd;
45 int mDispNum;
47 struct owlfb_disp_device * mDisplays[MAX_DISPLAY_NUMBER];
48 };
static int open_display_manager(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {
372 int status = -EINVAL;
373 struct disp_manager_context_t *ctx = static_cast<struct disp_manager_context_t *>(malloc(sizeof(struct disp_manager_context_t)));
376 memset(ctx, 0, sizeof(*ctx));
380 ctx->device.common.tag = HARDWARE_DEVICE_TAG;
381 ctx->device.common.version = 0;
382 ctx->device.common.module = const_cast<struct hw_module_t *>(module);
383 ctx->device.common.close = close_display_manager;
385 ctx->device.get_disp_info = owldisp_get_disp_info;
395 ctx->device.get_hdmi_cable_state =owldisp_get_hdmi_cable_state;
396 ctx->device.set_hdmi_fitscreen = owldisp_set_hdmi_fitscreen;
397 ctx->device.get_hdmi_fitscreen = owldisp_get_hdmi_fitscreen;
399 ctx->mDispFd = open(DM_HARDWARE_DEVICE, O_RDWR, 0);
401 init_display_device(ctx);
403 if (ctx->mDispFd >= 0) {
405 *device = &ctx->device.common;
}
这里的ctx->device.common其实就是hw_device_t,也就是说,返回的仅仅是我们定义的大结构体owldisp_device_t中的成员hw_device_t,而hw_device_t是owldisp_device_t的第一个成员,当强制类型转换发生时,外面接收owldisp_device_t这个大结构体的指针变量会将指针指到HAL层里面的有真正内容的owldisp_device_t这个大结构体中的hw_device_t的起始位置,其实也就是整个大结构体的起始位置,那这样的话,排在hw_device_t之后的所有成员就顺其自然的被外面接收owldisp_device_t这个大结构体的指针变量接纳了,所以看似只是返回了某个成员,而通过简单的一个强制类型转换就拿到了我们在HAL中自定义结构体owldisp_device_t的全部数据,当然包括了HAL提供出来的函数指针,神奇啊!
如果hw_device_t不是结构体owldisp_device_t的第一个成员,那么强制类型转换发生时,必然导致大结构体owldisp_device_t里面的数据的错乱甚至数据的丢失,这样的转换就没有意义了,HAL提供的接口也就彻底看不到了,那还要HAL层干嘛呢!
同理,hw_module_t 也必须是owldisp_module_t 的第一个成员,
62 owldisp_module_t * module;
69 if (hw_get_module(DM_HARDWARE_MODULE_ID, (const hw_module_t**) &module) == 0)
你看,传进去的module是owldisp_module_t 类型的,而hw_get_module(const char *id, const struct hw_module_t **module)的参数是固定的,必须是hw_module_t
所以hw_module_t 也必须是owldisp_module_t的第一个成员,这样返回后的强制类型转换才能正确指向owldisp_module_t类型的全部数据。
33 struct owldisp_module_t {
34 struct hw_module_t common;
35 };
四,问题验证
为了验证这个强制转换的正确性,特意写了一个实验程序,以验证上面的正确性:
#include <stdio.h>
#include <stdlib.h>
struct person {
int sno;
char sex;
int age;
};
struct student {
struct person per;
int val1;
int val2;
};
void test(struct person **per) {
struct student *stu = (struct student *)malloc(sizeof(struct student));
if (stu == NULL) {
printf("malloc failed!");
return;
}
stu->per.sno = 100201;
stu->per.sex = 'm';
stu->per.age = 32;
stu->val1 = 11;
stu->val2 = 22;
*per = &stu->per;
}
int main(int argc, char **argv) {
struct student *stu = NULL;
test((struct person **)&stu);
printf("student.val1: %d, student.per.sno: %d, student.per.age: %d \n", stu->val1, stu->per.sno, stu->per.age);
return 0;
}
当struct person是struct student的第一个成员时,如果这种强制转换是正确的,那么预期的输出应该是:
student.val1: 11, student.per.sno: 100201, student.per.age: 32
实际输出的确如此,所以结构体的第一个成员的位置在类似这种强制类型转换时的重要作用,得到了验证。其实说到底是因为结构体数据的内存连续性,因为这种连续性导致只要定位到了第一个成员的起始位置,也就等于获得了整个大结构体的全部数据;如果结构体在内存不是连续的,那HAL这个机制是玩不转的。
如果存在疑惑,马上可以再做如下实验:
将struct person作为struct student的第二个成员,其他代码保持不变:
struct student {
int val1;
struct person per;
int val2;
};
编译运行后的输出结果为:student.val1: 100201, student.per.sno: 109, student.per.age: 22,错乱了
将struct person作为struct student的第三个成员,其他代码保持不变:
struct student {
int val1;
int val2;
struct person per;
};
编译运行后的输出结果为:student.val1: 100201, student.per.sno: 32, student.per.age: 135137,错乱了。
- Android系统HAL层原理及编程注意事项
- android系统硬件抽象层(HAL)原理及实现之原理
- android系统硬件抽象层(HAL)原理及实现之原理
- android系统硬件抽象层(HAL)原理及实现之原理
- Android系统硬件抽象层(HAL)原理
- android系统开发--HAL层开发基础
- android系统开发--HAL层开发基础
- android系统开发--HAL层开发基础
- android系统开发-HAL层开发基础
- Android系统GPS HAL层移植笔记
- 关于android系统架构中的HAL层
- <4>Android HAL层 基础及调用
- Android HAL的作用及编程实例
- Android HAL的作用及编程实例
- Android HAL层基础
- Android HAL层HAL_MODULE_INFO
- android HAL层
- Android HAL层浅析
- 安卓常用的第三方框架
- synchronized用法与介绍
- Java内存模型总结
- 【IOS学习】理解UIScrollView
- 配置wifi为AP模式 -- 接入点hostapd基本配置
- Android系统HAL层原理及编程注意事项
- mysql group_contact 问题
- nil/Nil/NULL/NSNull的区别
- JAXB轻松转换xml对象和java对象
- There are 0 datanode(s) running and no node(s) are excluded in this operation.
- 2016.08.18【初中部 NOIP普及组 】模拟赛题解
- 设计模式六大原则
- MySQL无法启动Couldn't find MySQL server (/usr/bin/mysqld_safe)”
- 1021. 个位数统计 (15)