1.HAL的本质
HAL是将驱动的部分操作上移到用户空间,提供了基本的驱动操作(读写数据),这样做的目的是减少用户空间对kernel的依赖。用户空间将对驱动的操作交给HAL来完成。
2.基础数据结构
<1> struct hw_module_t 代表硬件设备所属的模块
<2> struct hw_module_methos_t代表模块中的方法
<3> struct hw_device_t 代表硬件设备的操作
3.数据结构之间的关系
hw_module_t 持有hw_module_methods_t指针
hw_device_t 持有hw_module_t指针
4.HAL的调用方式。
在Android4.2中,HAL将各个模块的动态库,隐藏起来。編譯的時候只是给需要调用HAL库的进程提供模块库的地址。執行的時候,再将库加载到内存,所以HAL的各个库文件在内存中始终只有一份拷贝,可以这么理解,HAL向其他进程提供库的代理,进程调用这些库的时候,讓代理完成具體的工作。在具体的調用過程中,這個代理就是庫的函數指針或者模块指针或者设备指针。
在获取模块的过程中,其他进程获取的是模块指针(hw_module_t)
在打开设备的过程中,其他进程获取的是设备指针(hw_device_t)
在向设备读写数据的过程中,操作的就是设备指针
5.自定义HAL
如果需要自定义HAL,正如上图所示的self_module_t,self_device_t
<1>
struct self_module_t{
hw_module_t common;
…..//自定义数据,与模块相关的函数指针
}
struct self_device_t{
hw_device_t common;
….//自定义数据,与设备驱动相关的函数指针
void (*f1)();
void (*f2)();
}
<2> 初始化self_module_t和self_module_methods_t,并在open方法中初始化self_device_t
static struct self_module_methods_tself_module_methods={
open: self_device_open
};
struct self_module_tHAL_MODULE_INFO_SYM={
.common:
{
tag: HARDWARE_MODULE_TAG,
version_major:1,
version_minor:0,
id:SELF_HARDWARE_MODULE_ID,
name:"Default SELF HW HAL",
author:"The Android Open SourceProject",
methods:&self_module_methods
},
};
static int self_device_open(conststruct hw_module_t * module,const char* name,hw_device_t** device){
struct self_device_t* dev;
dev=(structled_device_t*)malloc(sizeof(struct self_device_t));
if(!dev){
ALOGE("there is no enoughspace!");
return -EFAULT;
}
memset(dev,0,sizeof(structself_device_t));
dev->common.tag=HARDWARE_DEVICE_TAG;
dev->common.version=0;
dev->common.module=(hw_module_t*)module;
dev->common.close=self_device_close;
dev->f1=...//自己实现
dev->f2=...//自己实现
if((dev->fd=open(DEVICE_NAME,O_RDWR))==-1){
ALOGE("open failed");
return -EFAULT;
}
*device=&(dev->common);// 返回的是self_device_t中的hw_device_t的地址,也就是//self_device_t的首地址
return 0;
}
static int self_device_close(structhw_module_t* device){
struct self_device_t* self_device=(struct self_device_t)device;
if(led_device){
close(self_device->fd);
free(self_device);
}
return 0;
}
<3> 在JNI或者原生程序中调用 (通过hw_get_module方法获取自定义的模块部分的库的函数指针)
6.HAL的调用初始化过程
调用的初始化分为两部分,一是找到模块,二是打开模块的设备
从hw_get_module分析,它最终会将对应的库加载到内存,然后再通过调用open方法打开设备。
<1>调用hw_get_module的最终目的是为了活得模块的地址,以便打开模块中的设备。
<2>自己定义的modle变量名称必须是HAL_MODULE_INFO_SYM
<3>打开设备的过程的本质就是初始化一个self_device_t.
<4>初始化self_device_t,干了两件事,一是初始化hw_device_t,而是初始化self_device_t自己定义的函数指针。
// id是模块ID
inthw_get_module(constchar*id,conststructhw_module_t **module)
{
returnhw_get_module_by_class(id, NULL, module);
}
inthw_get_module_by_class(constchar*class_id,constchar*inst,
conststructhw_module_t **module)
{
intstatus;
inti;
conststructhw_module_t *hmi = NULL;
charprop[PATH_MAX];
charpath[PATH_MAX];
charname[PATH_MAX];
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 callingdlopen multiple times on
*the same .so will simply increment arefcount (and not load
*a new copy of the library).
*We also assume thatdlopen() 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_PATH2,name, prop);//根据模块ID,生成库的名称
if(access(path, R_OK) == 0)break;
snprintf(path,sizeof(path),"%s/%s.%s.so",
HAL_LIBRARY_PATH1,name, prop);
if(access(path, R_OK) == 0)break;
}else{
snprintf(path,sizeof(path),"%s/%s.default.so",
HAL_LIBRARY_PATH1,name);
if(access(path, R_OK) == 0)break;
}
}
staticintload(constchar*id,
constchar*path,
conststructhw_module_t **pHmi)
{
intstatus;
void*handle;
structhw_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) {
charconst*err_str = dlerror();
ALOGE("load:module=%s\n%s", path,err_str?err_str:"unknown");
status = -EINVAL;
gotodone;
}
/*Get the address of thestructhal_module_info. */
//
//这里是最关键的部分,HAL_MODULE_INFO_SYM_AS_STR是HMI,在库的二进制文件中可以找到这个///符号,对应的是hw_module_t的地址
//为了得到HMI,自己的modle的名称必须是HAL_MODULE_INFO_SYM
constchar*sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (structhw_module_t *)dlsym(handle, sym);
if(hmi == NULL) {
ALOGE("load:couldn't find symbol %s", sym);
status = -EINVAL;
gotodone;
}
/*Check that the id matches */
if(strcmp(id, hmi->id)!= 0) {
ALOGE("load:id=%s !=hmi->id=%s",id, hmi->id);
status = -EINVAL;
gotodone;
}
hmi->dso= handle;
/*success */
status = 0;
done:
if(status != 0) {
hmi = NULL;
if(handle != NULL) {
dlclose(handle);
handle = NULL;
}
}else{
ALOGV("loadedHAL id=%s path=%shmi=%phandle=%p",
id, path, *pHmi,handle);
}
*pHmi = hmi;//模块的地址就可以通过指针返回了
returnstatus;
}