分享

Android中的HAL相关库搜索机制和原理学习

 astrotycoon 2018-04-27

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢。

欢迎和大家交流。qq:1037701636 email:gzzaigcn2012@gmail.com

Android源码版本Version:4.2.2; 硬件平台 全志A31

 

在介绍FrameWork是不得不提的是HAL(硬件抽象层)一般是用来和特点的硬件平台进行交互的,所以不同的android平台主要的区别也就是在这个部分,HAL的好处在于一个FrameWork可以调用不同的HAL,只需要相关的HAL满足一定接口规范即可。另一方面HAL的好处是可以屏蔽相关对底层硬件操作的应用代码。

网上对HAL的介绍内容已经很多,这里就简单和大家分享我所深入去了解的HAL层的相关规范。

核心的几个API:hw_get_module(), hw_get_module_by_class(), load()就这么简单,核心在hw_get_module_by_class(),来看其实现的过程代码:

  1. <span style="font-size:14px;">int hw_get_module_by_class(const char *class_id, const char *inst,  
  2.                            const struct hw_module_t **module)  
  3. {  
  4.     int status;  
  5.     int i;  
  6.     const struct hw_module_t *hmi = NULL;  
  7.     char prop[PATH_MAX];  
  8.     char path[PATH_MAX];  
  9.     char name[PATH_MAX];  
  10.   
  11.     if (inst)  
  12.         snprintf(name, PATH_MAX, "%s.%s", class_id, inst);  
  13.     else  
  14.         strlcpy(name, class_id, PATH_MAX);  
  15.   
  16.     /* 
  17.      * Here we rely on the fact that calling dlopen multiple times on 
  18.      * the same .so will simply increment a refcount (and not load 
  19.      * a new copy of the library). 
  20.      * We also assume that dlopen() is thread-safe. 
  21.      */  
  22.   
  23.     /* Loop through the configuration variants looking for a module */  
  24.     for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {  
  25.         if (i < HAL_VARIANT_KEYS_COUNT) {  
  26.             if (property_get(variant_keys[i], prop, NULL) == 0) {  
  27.                 continue;  
  28.             }  
  29.             snprintf(path, sizeof(path), "%s/%s.%s.so",  
  30.                      HAL_LIBRARY_PATH2, name, prop);  
  31.             if (access(path, R_OK) == 0) break;//设备厂商特定的HAL库所在目录/vendor/lib/hw  
  32.   
  33.             snprintf(path, sizeof(path), "%s/%s.%s.so",  
  34.                      HAL_LIBRARY_PATH1, name, prop);//通用的HAL库/system/lib/hw  
  35.             if (access(path, R_OK) == 0) break;  
  36.         } else {  
  37.             snprintf(path, sizeof(path), "%s/%s.default.so",  
  38.                      HAL_LIBRARY_PATH1, name);//默认库  
  39.             if (access(path, R_OK) == 0) break;  
  40.         }  
  41.     }  
  42.   
  43.     status = -ENOENT;  
  44.     if (i < HAL_VARIANT_KEYS_COUNT+1) {  
  45.         /* load the module, if this fails, we're doomed, and we should not try 
  46.          * to load a different variant. */  
  47.         status = load(class_id, path, module);  
  48.     }  
  49.   
  50.     return status;  
  51. }  
  52. </span>  

step1:关注变量variant_keys, 通过系统属性获取函数property_get()来获取下面4个属性变量在内存的系统属性中维护的数值(其中系统属性的创建和维护主要由init进程来完成)

  1. <span style="font-size:14px;">static const char *variant_keys[] = {  
  2.     "ro.hardware",  /* This goes first so that it can pick up a different 
  3.                        file on the emulator. */  
  4.     "ro.product.board",  
  5.     "ro.board.platform",  
  6.     "ro.arch"  
  7. };  
  8. </span>  

先来看ro.hardware,它一般是指定了系统板级的硬件如我这里的sun6i, 那这个属性变量在何处设置呢,首先出现的地方是在init进程里面,依次调用如下:

1.get_hardware_name()——> fd = open("/proc/cpuinfo", O_RDONLY):搜索cpuinfo里面的hardware字段,有的话就保存在hardware中

2.process_kernel_cmdline——>export_kernel_boot_props():这里会对boot启动时设置的属性值查询,对hardware有如下代码:

  1. <span style="font-size:14px;"> /* if this was given on kernel command line, override what we read 
  2.      * before (e.g. from /proc/cpuinfo), if anything */  
  3.     pval = property_get("ro.boot.hardware");  
  4.     if (pval)  
  5.         strlcpy(hardware, pval, sizeof(hardware));  
  6.     property_set("ro.hardware", hardware);</span>  

如果在boot时设置了ro.boot.hardware那么hardware就重载从/proc/cpuinfo里面读取的数值,这样整个ro.hardware的硬件系统属性值就配置好了。

 

对于"ro.product.board"、 "ro.board.platform"、"ro.arch"这3个属性变量,就要集中到android的属性文件上来了,android系统集中的属性文件有以下几个:

android的基本属性文件目录:
#define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"
#define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"
#define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"
#define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"
以上文件将会在init进程里面得到进一步的解析,关注到如下的代码:
  1. queue_builtin_action(property_service_init_action, "property_service_init");//属性服务的初始化  
属性服务初始化action被加入到action队列等待执行,执行函数execute_one_command,最终会回调上面注册的action内的property_service_init_action,这里就能看到属性服务的从文件内部读取维护到内存中:
  1. void start_property_service(void)  
  2. {  
  3.     int fd;  
  4.   
  5.     load_properties_from_file(PROP_PATH_SYSTEM_BUILD);  
  6.     load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);  
可以看到会解析不同路径下的prop属性文件,包括下面要介绍的这个buil.prop
我们来看看/system/build.prop是由Android编译时由编译脚本build/core/Makefile和Shell脚本build/tools/buildinfo.sh来生成的,综合了整个编译配置环境下的平台相关变量,而这些变量往往在Android系统的关于设备信息中都能查看的到;
  1.   3 ro.build.id=JDQ39  
  2.   4 ro.build.display.id=fiber_3g-eng 4.2.2 JDQ39 20140110 test-keys  
  3.   5 ro.build.version.incremental=20140110  
  4.   6 ro.build.version.sdk=17  
  5.   7 ro.build.version.codename=REL  
  6.   8 ro.build.version.release=4.2.2  
  7.   9 ro.build.date=2014年 01月 10日 星期五 16:03:07 CST  
  8.  10 ro.build.date.utc=1389340987  
  9.  11 ro.build.type=eng  
  10.  12 ro.build.user=root  
  11.  13 ro.build.host=linux  
  12.  14 ro.build.tags=test-keys  
  13.  15 ro.product.model=Softwinner  
  14.  16 ro.product.brand=Softwinner  
  15.  17 ro.product.name=fiber_3g  
  16.  18 ro.product.device=fiber-3g  
  17.  19 ro.product.board=exdroid  
  18.  20 ro.product.cpu.abi=armeabi-v7a  
  19.  21 ro.product.cpu.abi2=armeabi  
  20.  22 ro.product.manufacturer=unknown  
  21.  23 ro.product.locale.language=en  
  22.  24 ro.product.locale.region=US  
  23.  25 ro.wifi.channels=  
  24.  26 ro.board.platform=fiber  
  25. ....  
好了上面介绍了那么多,其实已经涵盖并跳跃了很多的内容,我们回归到hw_get_module_by_class函数,下面就是确定在哪些目录下进行优先的搜索,可以看到以下2个主要路径:
/** Base path of the hal modules */
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
故而可知,当前的hal库一般即只能放在这下面,否则找不到程序肯定不能正常运行,的确在编译时我们不得不去手写Android.mk里面添加cp指令,完成当前库的编译并copy到上述2个目录,一般与硬件平台更密切的库放于/vendor/lib/hw,比如gralloc.sun6i.so  hwcomposer.sun6i.so等就是这样被找到的。
step2.  在完成搜索并定位到了对应库的路径之后,下面就是动态加载
load函数,主要将库文件加载到,HMI所在的地址:
#define HAL_MODULE_INFO_SYM_AS_STR  "HMI"
handle = dlopen(path, RTLD_NOW);
hmi = (struct hw_module_t *)dlsym(handle, sym);
这样就获取了当前库文件的struct hal_module_info的信息即hw_m odule_t(内部有一个hw_module_methods_t* methods内部的open函数会向FW提供一个设备操作接口hw_devices_t),最终返回后就可以通过它操作相关的HAL中的封装函数。
本文的重点是分析HAL的搜索以及定位HAL的过程,以便后续自己完成Android的定制。
    handle = dlopen(path, RTLD_NOW);

 

 

 




 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多