Android HAL 是如何被调用的

Android 对硬件的调用, google 推荐使用 HAL 的方式进行调用,对于 Andriod HAL 的写法,可以参考 android 源码里的 hardware 目录下几个模块的模版。

在看 HAL 的编写方法的过程中,会发现整个模块貌似没有一个入口。一般说来模块都要有个入口,比如应用程序有 main 函数,可以为加载器进行加载执行, dll 文件有 dllmain ,而对于我们自己写的动态链接库,我们可以对库中导出的任何符号进行调用。

问题来了, Android 中的 HAL 是比较具有通用性的,需要上层的函数对其进行加载调用, Android 的 HAL 加载器是如何实现对不同的 Hardware Module 进行通用性的调用的呢?

带着这个疑问查看 Android 源码,会发现 Android 中实现调用 HAL 是通过 hw_get_module 实现的。

int hw_get_module(const char *id, const struct hw_module_t **module);

这是其函数原型, id 会指定 Hardware 的 id ,这是一个字符串,比如 sensor 的 id 是

#define SENSORS_HARDWARE_MODULE_ID "sensors" 

如果找到了对应的 hw_module_t 结构体,会将其指针放入 *module 中。看看它的实现。。。。

[cpp]   view plain copy print ?
  1.   
  2. for (i=0 i<HAL_VARIANT_KEYS_COUNT+1 i++)  
  3.     if (i HAL_VARIANT_KEYS_COUNT)  
  4.         //获取ro.hardware/ro.product.board/ro.board.platform/ro.arch等key的值。  
  5.         if (property_get(variant_keys[i], prop, NULL) == 0)  
  6.             continue 
  7.          
  8.         snprintf(path, sizeof(path), "%s/%s.%s.so" 
  9.                 HAL_LIBRARY_PATH, id, prop);  
  10.          //如果开发板叫做mmdroid,那么这里的path就是system/lib/hw/sensor.mmdroid.so  
  11.     else  
  12.         snprintf(path, sizeof(path), "%s/%s.default.so" 
  13.                 HAL_LIBRARY_PATH, id);//默认会加载/system/lib/hw/sensor.default.so  
  14.   
  15.      
  16.     if (access(path, R_OK))  
  17.         continue 
  18.      
  19.       
  20.     break 
  21.  
  22. status -ENOENT;  
  23. if (i HAL_VARIANT_KEYS_COUNT+1)  
  24.       
  25.     status load(id, path, module);//调用load函数打开动态链接库  
  26.  

获取了动态链接库的路径之后,就会调用 load 函数打开它,下面会打开它。奥秘在 load 中

[cpp]   view plain copy print ?
  1. static int load(const char *id,  
  2.         const char *path,  
  3.         const struct hw_module_t **pHmi)  
  4.  
  5.     int status;  
  6.     void *handle;  
  7.     struct hw_module_t *hmi;  
  8.    
  9.       
  10.     handle dlopen(path, RTLD_NOW);//打开动态库  
  11.     if (handle == NULL)  
  12.         char const *err_str dlerror();  
  13.         LOGE("load: module=%s/n%s"path, err_str?err_str:"unknown");  
  14.         status -EINVAL;  
  15.         goto done;  
  16.      
  17.    
  18.       
  19.     const char *sym HAL_MODULE_INFO_SYM_AS_STR;//被定义为了“HMI”  
  20.     hmi (struct hw_module_t *)dlsym(handle, sym);//查找“HMI”这个导出符号,并获取其地址  
  21.     if (hmi == NULL)  
  22.         LOGE("load: couldn't find symbol %s"sym);  
  23.         status -EINVAL;  
  24.         goto done;  
  25.      
  26.    
  27.       
  28. //找到了hw_module_t结构!!!  
  29.     if (strcmp(id, hmi->id) != 0)  
  30.         LOGE("load: id=%s != hmi->id=%s"id, hmi->id);  
  31.         status -EINVAL;  
  32.         goto done;  
  33.       
  34.     hmi->dso handle;  
  35.         
  36.     status 0;  
  37. done:  
  38.     if (status != 0)  
  39.         hmi NULL;  
  40.         if (handle != NULL)  
  41.             dlclose(handle);  
  42.             handle NULL;  
  43.          
  44.     else  
  45.         LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p" 
  46.                 id, path, *pHmi, handle);  
  47.      
  48.   //凯旋而归  
  49.     *pHmi hmi;  
  50.      return status;  
  51.  

从上面的代码中,会发现一个很奇怪的宏 HAL_MODULE_INFO_SYM_AS_STR ,它直接被定义为了

#define HAL_MODULE_INFO_SYM_AS_STR "HMI" 

为何根据它就能从动态链接库中找到这个 hw_module_t 结构体呢?我们查看一下我们用到的 hal 对应的 so 就可以了,在 linux 中可以使用 readelf XX.so –s 查看。

[plain]   view plain copy print ?
  1. Symbol table '.dynsym' contains 28 entries:  
  2.    Num:    Value Size Type    Bind   Vis      Ndx Name  
  3.      0: 00000000     NOTYPE  LOCAL  DEFAULT  UND  
  4.      1: 00000594     SECTION LOCAL  DEFAULT     
  5.      2: 00001104     SECTION LOCAL  DEFAULT   13  
  6.      3: 00000000     FUNC    GLOBAL DEFAULT  UND ioctl  
  7.      4: 00000000     FUNC    GLOBAL DEFAULT  UND strerror  
  8.      5: 00000b84     NOTYPE  GLOBAL DEFAULT  ABS __exidx_end  
  9.      6: 00000000     OBJECT  GLOBAL DEFAULT  UND __stack_chk_guard  
  10.      7: 00000000     FUNC    GLOBAL DEFAULT  UND __aeabi_unwind_cpp_pr0  
  11.      8: 00000000     FUNC    GLOBAL DEFAULT  UND __errno  
  12.      9: 00001188     NOTYPE  GLOBAL DEFAULT  ABS _bss_end__  
  13.     10: 00000000     FUNC    GLOBAL DEFAULT  UND malloc  
  14.     11: 00001188     NOTYPE  GLOBAL DEFAULT  ABS __bss_start__  
  15.     12: 00000000     FUNC    GLOBAL DEFAULT  UND __android_log_print  
  16.     13: 00000b3a     NOTYPE  GLOBAL DEFAULT  ABS __exidx_start  
  17.     14: 00000000     FUNC    GLOBAL DEFAULT  UND __stack_chk_fail  
  18.     15: 00001188     NOTYPE  GLOBAL DEFAULT  ABS __bss_end__  
  19.     16: 00001188     NOTYPE  GLOBAL DEFAULT  ABS __bss_start  
  20.     17: 00000000     FUNC    GLOBAL DEFAULT  UND memset  
  21.     18: 00000000     FUNC    GLOBAL DEFAULT  UND __aeabi_uidiv  
  22.     19: 00001188     NOTYPE  GLOBAL DEFAULT  ABS __end__  
  23.     20: 00001188     NOTYPE  GLOBAL DEFAULT  ABS _edata  
  24.     21: 00001188     NOTYPE  GLOBAL DEFAULT  ABS _end  
  25.     22: 00000000     FUNC    GLOBAL DEFAULT  UND open  
  26.     23: 00080000     NOTYPE  GLOBAL DEFAULT  ABS _stack  
  27.     24: 00001104  128 OBJECT  GLOBAL DEFAULT   13 HMI  
  28.     25: 00001104     NOTYPE  GLOBAL DEFAULT   13 __data_start  
  29.     26: 00000000     FUNC    GLOBAL DEFAULT  UND close  
  30.     27: 00000000     FUNC    GLOBAL DEFAULT  UND free  

从上面中,第24个符号,名字就是“HMI”,对应于hw_module_t结构体。再去对照一下HAL的代码。

[cpp]   view plain copy print ?
  1.   
  2. struct copybit_module_t HAL_MODULE_INFO_SYM  
  3.     common:  
  4.         tag: HARDWARE_MODULE_TAG,  
  5.         version_major: 1,  
  6.         version_minor: 0,  
  7.         id: COPYBIT_HARDWARE_MODULE_ID,  
  8.         name: "QCT MSM7K COPYBIT Module" 
  9.         author: "Google, Inc." 
  10.         methods: ©bit_module_methods  
  11.      
  12. };  

这里定义了一个名为 HAL_MODULE_INFO_SYM 的 copybit_module_t 的结构体, common 成员为 hw_module_t 类型。注意这里的 HAL_MODULE_INFO_SYM 变量必须为这个名字,这样编译器才会将这个结构体的导出符号变为“ HMI ”,这样这个结构体才能被 dlsym 函数找到!

综上,我们知道了 andriod HAL 模块也有一个通用的入口地址,这个入口地址就是 HAL_MODULE_INFO_SYM 变量,通过它,我们可以访问到 HAL 模块中的所有想要外部访问到的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值