android系统开发--HAL层开发基础
android HAL浅探
又挖一个坑,好久没写了,看得代码多而繁乱,又没有专精一块,到头来还是困惑丛生,不管了,做个笔记,写写自己对android hal层的一点理解。
涉及的代码来自android-4.0版本。
1.概念和原因
android hal的概念和存在的原因,不必多说,其实就两点。1.隔离具体驱动接口的变化,2.保护硬件厂商的关键信息。
通过这一层的封装
1.各类硬件(fb,gps,sensor)只需要实现规定的接口即可,驱动接口的不兼容(比如各种硬件设备的ioctl,很可能是厂商自己规定的命令),可以隔离变化。
2.硬件厂商在封装的接口,只需提供一个so即可,使用时,用dlopen hook到相关的接口,不用开放源代码,可以保护一些敏感信息。
总体来说,这层位于kernel/driver之上,framework层之下。
(都是废话,没有干货,呵呵)
2.硬件相关代码位置
在hardware/libhardware/include/hardware目录里面,可以看到目前使用这种方式抽象的硬件种类大致有如下几种。(btw,android貌似还有另外的方式操作硬件,具体的我没看明白,貌似wifi的方式就不太一样,好像是通过协议栈来操作的,对这块不熟悉,只是随便看了看)
audio(声卡,我姑且这么叫,这块驱动层次都有若干标准,让人头晕眼花,比如oss/alsa/jack/等等等。。。这还不是最可恶的,他们还可以互为后端,比oss接口封装成alsa,或者反过来,我去。。。)
camera(摄像头,不多说, 主要我也不太懂,这块linux到是有标准接口,v4l2--->video for linux 2,移植所需要做的工作就是把v4l2接口封装成android规定的,呵呵)
gps(定位,这个真是一点没研究过了)
sensor(传感器,指南针,重力加速度,压力,位置,。。。)
lights(背光,调节背光强弱的)
display(显示相关的几个设备,fb-->framebuffer,应该对应的是屏幕,仅仅是屏幕,不包括显存,gralloc这个设备,才对应的是真正的显存及其操作,hwcomposer,其实就是overlay了,播放视频时需要,ps:这里的display仅仅指lcd,不包括hdmi,vga等等,呵呵)
3.display设备被调用的方式
我主要看了display所需要的硬件设备,其他设备,精力和兴趣的原因,没有多看。
android display最终的显示的数据都会交给surfaceflinger(直译,屏幕投递者,呵呵),所以操作display硬件的代码这里一定会有。
在framework/base/services/surfaceflinger/displayhardware目录下面,封装了这部分接口,我们可以通过研究这块接口封装,来看到具体硬件是如何被抽象的。
首先,我对surfaceflinger也没非常通彻的理解,只了解一个大概,所以,我只说流程,不说细节。
1.先看surfaceflinger构造函数能不能发现什么,貌似没有发现new displayhardware之类的语句,那这些操作在哪里?我们知道surfaceflinger从Thread和sp继承,那还有两个地方可以去瞧瞧。
1).onfirstref,sp模板构造出来的类,这个函数是首先会被调用的,不过貌似这里也没有,只有一个run(启动线程)
2).readyToRun,这个函数,在run之前一定会被调用一把的,进去一看,发现我们想要的DisplayHardware* const hw = new DisplayHardware(this, dpy)语句,哈哈,这里构造了display硬件的对应的类。
2.DisplayHardware构造函数
这个类上面所说的三类硬件,又归结了两类,
1).FramebufferNativeWindow(这个对应了fb与gralloc,抽象的具体代码在framework/base/libs/ui,这个库里面包括了ui相关所有东西,显示,输入消息,按键映射等等),
2).HWComposer(这个对应了hwcomposer)
(感觉都是一堆废话,都明白的东西,居然还没讲到如何调用)
好吧,我们再简单一点,直接进去看hwcomposer是如何调用的
hardware.h里面给了两个函数,
hw_get_module
hw_get_module_by_class
具体就是,你传一个硬件id(每种硬件,对应一个id,id是字符串,各不相同)下来,我把这个module对应的接口给你。
来看hwcomposer的构造函数。里面调用了hw_get_module,他对应的id是HWC_HARDWARE_MODULE_ID,
在hardware/libhardware/include/hwcomposer.h看到 #define HWC_HARDWARE_MODULE_ID "hwcomposer"
同时其他对应的文件里面,fb的id是 #define GRALLOC_HARDWARE_FB0 "fb0"
gralloc的id是 #define GRALLOC_HARDWARE_MODULE_ID "gralloc"
我们看看最关键的地方hw_get_module是如何实现的。这个关键点在于load函数。
load函数dlopen相关的vendor库,然后通过dlsym找一个HAL_MODULE_INFO_SYM_AS_STR的全局变量(#define HAL_MODULE_INFO_SYM_AS_STR "HMI",其实就是对应了HAL_MODULE_INFO_SYM这个)
这个我认为是最关键的点,就找这个全局变量,所有设备编译出来的so库,都要包括这个hmi的变量,可以通过查看代码来验证这个猜测。
这个地方找到之后,就完全了软硬件合体了。
这里不仅可以找到common的open接口,也可以找到各种私有接口,
比如fb就规定了六个接口
int (*setSwapInterval)(struct framebuffer_device_t* window,int interval);
int (*setUpdateRect)(struct framebuffer_device_t* window,int left, int top, int width, int height);
int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);
int (*compositionComplete)(struct framebuffer_device_t* dev);
void (*dump)(struct framebuffer_device_t* dev, char *buff, int buff_len);
int (*enableScreen)(struct framebuffer_device_t* dev, int enable);
底层实现这几个接口即可。
看完overlay硬件的抽象方式,再按这个思路去看fb,gralloc,gps,会发现他们的过程完全一致。也许大家一直这么做的,但是我是从android里面知道这样的隔离变化方案的,很好很强大。谢谢谷歌。
over。
有点乱,过程就是这样了,自己的笔记,不喜勿喷。
==================================================================================================
Android HAL层,即硬件抽象层,是Google响应厂家“希望不公开源码”的要求推出的新概念
1,源代码和目标位置
源代码: /hardware/libhardware目录,该目录的目录结构如下:
/hardware/libhardware/hardware.c编译成libhardware.so,目标位置为/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目录下定义了很多硬件模块
这些硬件模块都编译成xxx.xxx.so,目标位置为/system/lib/hw目录
2,HAL层的实现方式
JNI->通用硬件模块->硬件模块->内核驱动接口
具体一点:JNI->libhardware.so->xxx.xxx.so->kernel
具体来说:android frameworks中JNI调用/hardware/libhardware/hardware.c中定义的hw_get_module函数来获取硬件模块,
然后调用硬件模块中的方法,硬件模块中的方法直接调用内核接口完成相关功能
3,通用硬件模块(libhardware.so)
(1)头文件为:/hardware/libhardware/include/hardware/hardware.h
头文件中主要定义了通用硬件模块结构体hw_module_t,声明了JNI调用的接口函数hw_get_module
hw_module_t定义如下:
typedef struct hw_module_t {
/** tag must be initialized to HARDWARE_MODULE_TAG */
uint32_t 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;
/** 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_methods_t定义如下:
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;
只定义了一个open方法,其中调用的设备结构体参数hw_device_t定义如下:
typedef struct hw_device_t {
/** tag must be initialized to HARDWARE_DEVICE_TAG */
uint32_t 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;
hw_get_module函数声明如下:
int hw_get_module(const char *id, const struct hw_module_t **module);
参数id为模块标识,定义在/hardware/libhardware/include/hardware目录下的硬件模块头文件中,
参数module是硬件模块地址,定义了/hardware/libhardware/include/hardware/hardware.h中
(2)hardware.c中主要是定义了hw_get_module函数如下:
#define HAL_LIBRARY_PATH "/system/lib/hw"
static const char *variant_keys[] = {
"ro.hardware",
"ro.product.board",
"ro.board.platform",
"ro.arch"
};
static const int HAL_VARIANT_KEYS_COUNT =
(sizeof(variant_keys)/sizeof(variant_keys[0]));
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];
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_PATH, id, prop);
}
else
{
snprintf(path, sizeof(path), "%s/%s.default.so",
HAL_LIBRARY_PATH, id);
}
if (access(path, R_OK))
{
continue;
}
/* we found a library matching this id/variant */
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);
}
return status;
}
从源代码我们可以看出,hw_get_module完成的主要工作是根据模块id寻找硬件模块动态连接库地址,然后调用load函数去打开动态连接库
并从动态链接库中获取硬件模块结构体地址。硬件模块路径格式如下:
HAL_LIBRARY_PATH/id.prop.so
HAL_LIBRARY_PATH定义为/system/lib/hw
id是hw_get_module函数的第一个参数所传入,prop部分首先按照variant_keys数组中的名称逐一调用property_get获取对应的系统属性,
然后访问HAL_LIBRARY_PATH/id.prop.so,如果找到能访问的就结束,否则就访问HAL_LIBRARY_PATH/id.default.so
举例如下:
假定访问的是背光模块,id定义为"lights"则系统会按照如下的顺序去访问文件:
/system/lib/hw/lights.[ro.hardware属性值].so
/system/lib/hw/lights.[ro.product.board属性值].so
/system/lib/hw/lights.[ro.board.platform属性值].so
/system/lib/hw/lights.[ro.arch属性值].so
/system/lib/hw/lights.default.so
所以开发硬件模块的时候Makefile文件(Android.mk)中模块的命名LOCAL_MODULE要参考上面的内容,否则就会访问不到没作用了。
load函数的关键部分代码如下:
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;
}
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;
}
HAL_MODULE_INFO_SYM_AS_STR是硬件模块在动态链接库中的标志,定义在hardware.h中如下:
#define HAL_MODULE_INFO_SYM HMI
#define HAL_MODULE_INFO_SYM_AS_STR "HMI"
4,硬件模块
硬件模块的开发主要是完成/hardware/libhardware/include/hardware目录下对应的头文件中的内容,主要是硬件模块头文件和hardware.h中
的结构体中定义了一些函数指针,调用内核提供的接口将具体的函数实现,然后编译成指定名称的动态链接库放到/system/lib/hw目录下即可。
用一句话来概括:硬件模块的开发就是定义一个hardware.h中定义的hw_module_t结构体,结构体名称为宏HAL_MODULE_INFO_SYM,然后实现结构体
的相关内容即可。
5,内核驱动
主要是要向用户层开放接口,让硬件模块和内核可以交互。