Sensor----HAL

原文:https://blog.csdn.net/huilin9960/article/details/80578729

 

 

Android Native到HAL源码剖析,以sensor为例

2018年06月05日 13:54:26 剥皮包子 阅读数:1136更多

所属专栏: Android Sensor架构全解析

 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huilin9960/article/details/80578729

Google为了保护硬件厂商的信息,在Android中添加了一层,它就是大名鼎鼎的HAL层。

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

那么问题来了,Android中的HAL是具有通用性的,需要上层的函数对其进行加载调用,Android的HAL加载器是如何实现对不同的Hardware Module进行通用性的调用的呢?我们今天将以Sensor hal模块的加载为例,剖析下hal层的具体调用逻辑。

SensorService启动

在看Sensor架构的时候,SensorService服务启动后,在随后的第一次初始化时,其onFirstRef会被调用(均继承自RefBase虚基类),紧接着,它会获取我们的SensorDevice实例:

 
  1. void SensorService::onFirstRef()

  2. {

  3. ALOGD("nuSensorService starting...");

  4. SensorDevice& dev(SensorDevice::getInstance());

  5.  
  6. if (dev.initCheck() == NO_ERROR) {

  7. sensor_t const* list;

  8. ssize_t count = dev.getSensorList(&list);

附上这部分的流程

SensorDevice作为Sensor架构中native的最后一个文件,与Hal层进行通信,故而在SensorDevice的构造方法中,我们就可以看到著名的hw_get_module和Sensor_Open方法了:

 
  1. SensorDevice::SensorDevice()

  2. : mSensorDevice(0),

  3. mSensorModule(0)

  4. {

  5. status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,

  6. (hw_module_t const**)&mSensorModule);

  7.  
  8. ALOGE_IF(err, "couldn't load %s module (%s)",

  9. SENSORS_HARDWARE_MODULE_ID, strerror(-err));

  10.  
  11. if (mSensorModule) {

  12. err = sensors_open_1(&mSensorModule->common, &mSensorDevice);

  13.  
  14. ALOGE_IF(err, "couldn't open device for module %s (%s)",

  15. SENSORS_HARDWARE_MODULE_ID, strerror(-err));

  16.  
  17. if (mSensorDevice) {

  18. if (mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_1 ||

  19. mSensorDevice->common.version == SENSORS_DEVICE_API_VERSION_1_2) {

  20. ALOGE(">>>> WARNING <<< Upgrade sensor HAL to version 1_3");

  21. }

  22.  
  23. sensor_t const* list;

  24. ssize_t count = mSensorModule->get_sensors_list(mSensorModule, &list);

  25. mActivationCount.setCapacity(count);

  26. Info model;

  27. for (size_t i=0 ; i<size_t(count) ; i++) {

  28. mActivationCount.add(list[i].handle, model);

  29. mSensorDevice->activate(

  30. reinterpret_cast<struct sensors_poll_device_t *>(mSensorDevice),

  31. list[i].handle, 0);

  32. }

  33. }

  34. }

  35. }

其中SENSORS_HARDWARE_MODULE_ID是在hardware/sensors.h中定义的module名字:

 
  1. /**

  2. * The id of this module

  3. */

  4. #define SENSORS_HARDWARE_MODULE_ID "sensors"

而mSensorModule就是我们的sensors_module_t结构体,这些都是在hal层sensors.h中定义的:

 
  1. struct sensors_module_t {

  2. struct hw_module_t common;

  3.  
  4. /**

  5. * Enumerate all available sensors. The list is returned in "list".

  6. * @return number of sensors in the list

  7. */

  8. int (*get_sensors_list)(struct sensors_module_t* module,

  9. struct sensor_t const** list);

  10.  
  11. /**

  12. * Place the module in a specific mode. The following modes are defined

  13. *

  14. * 0 - Normal operation. Default state of the module.

  15. * 1 - Loopback mode. Data is injected for the the supported

  16. * sensors by the sensor service in this mode.

  17. * @return 0 on success

  18. * -EINVAL if requested mode is not supported

  19. * -EPERM if operation is not allowed

  20. */

  21. int (*set_operation_mode)(unsigned int mode);

  22. };

可以看到sensors_module_t结构体扩展了hw_module_t,他里面额外提供了get_sensor_list方法来获取系统支持的sensor列表以及一个模式设置方法。

接下来,我们跟进hw_get_module方法,看看它到底做了什么?

hw_get_module

该函数具体实现在hardware/libhardware/hardware.c中

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

  2. {

  3. return hw_get_module_by_class(id, NULL, module);

  4. }

 
  1. int hw_get_module_by_class(const char *class_id, const char *inst,

  2. const struct hw_module_t **module)

  3. {

  4. int i = 0;

  5. char prop[PATH_MAX] = {0};

  6. char path[PATH_MAX] = {0};

  7. char name[PATH_MAX] = {0};

  8. char prop_name[PATH_MAX] = {0};

  9.  
  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. /* First try a property specific to the class and possibly instance */

  24. snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);

  25. if (property_get(prop_name, prop, NULL) > 0) {

  26. if (hw_module_exists(path, sizeof(path), name, prop) == 0) {

  27. goto found;

  28. }

  29. }

  30.  
  31. /* Loop through the configuration variants looking for a module */

  32. for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {

  33. if (property_get(variant_keys[i], prop, NULL) == 0) {

  34. continue;

  35. }

  36. if (hw_module_exists(path, sizeof(path), name, prop) == 0) {

  37. goto found;

  38. }

  39. }

  40.  
  41. /* Nothing found, try the default */

  42. if (hw_module_exists(path, sizeof(path), name, "default") == 0) {

  43. goto found;

  44. }

  45.  
  46. return -ENOENT;

  47.  
  48. found:

  49. /* load the module, if this fails, we're doomed, and we should not try

  50. * to load a different variant. */

  51. return load(class_id, path, module);

  52. }

我们主要看hw_get_module_by_class,这里传入的参数分别是“sensors”,null,以及我们的mSensorModule结构体。

首先将字符串拷贝给name:

strlcpy(name, class_id, PATH_MAX);

接着拼接prop_name为ro.hardware.name,即prop_name=ro.hardware.sensors

通过property_get方法并没有得到这个值的定义(因为在系统中并没有对其定义),所以接下来会进入下面的循环:

 
  1. for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {

  2. if (property_get(variant_keys[i], prop, NULL) == 0) {

  3. continue;

  4. }

  5. if (hw_module_exists(path, sizeof(path), name, prop) == 0) {

  6. goto found;

  7. }

  8. }

 
  1. /**

  2. * There are a set of variant filename for modules. The form of the filename

  3. * is "<MODULE_ID>.variant.so" so for the led module the Dream variants

  4. * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:

  5. *

  6. * led.trout.so

  7. * led.msm7k.so

  8. * led.ARMV6.so

  9. * led.default.so

  10. */

  11.  
  12. static const char *variant_keys[] = {

  13. "ro.hardware", /* This goes first so that it can pick up a different

  14. file on the emulator. */

  15. "ro.product.board",

  16. "ro.board.platform",

  17. "ro.arch"

  18. };

根据上面的解析我门也可以看到,将会分别查找sensors.variant.so,sensors.product.so,sensors.platform.so,以及sensors.default.so,最终我们会在/system/lib/hw/路径下找到sensors.msm8952.so,然后将其通过load方法加载进内存中运行。由此也可知,我分析的是高通8952平台。

小细节:当我们实现了自己的HAL层module,并且写了一个应用程序测试module是否正常工作,那么在编译的时候,下面的参数应该要这样写:

LOCAL_MODULE := moduleName.default

或者

LOCAL_MODULE := moduleName.$(TARGET_BOARD_PLATFORM)

由于上面源码的原因,如果module名字对应不到,你的这个模块将不会被正常的load进去,因而也就无法正常工作了。

接着我们分析load的实现。

 
  1. static int load(const char *id,

  2. const char *path,

  3. const struct hw_module_t **pHmi)

  4. {

  5. int status = -EINVAL;

  6. void *handle = NULL;

  7. struct hw_module_t *hmi = NULL;

  8.  
  9. /*

  10. * load the symbols resolving undefined symbols before

  11. * dlopen returns. Since RTLD_GLOBAL is not or'd in with

  12. * RTLD_NOW the external symbols will not be global

  13. */

  14. handle = dlopen(path, RTLD_NOW);

  15. if (handle == NULL) {

  16. char const *err_str = dlerror();

  17. ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");

  18. status = -EINVAL;

  19. goto done;

  20. }

  21.  
  22. /* Get the address of the struct hal_module_info. */

  23. const char *sym = HAL_MODULE_INFO_SYM_AS_STR;

  24. hmi = (struct hw_module_t *)dlsym(handle, sym);

  25. if (hmi == NULL) {

  26. ALOGE("load: couldn't find symbol %s", sym);

  27. status = -EINVAL;

  28. goto done;

  29. }

  30.  
  31. /* Check that the id matches */

  32. if (strcmp(id, hmi->id) != 0) {

  33. ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);

  34. status = -EINVAL;

  35. goto done;

  36. }

  37.  
  38. hmi->dso = handle;

  39.  
  40. /* success */

  41. status = 0;

  42.  
  43. done:

  44. if (status != 0) {

  45. hmi = NULL;

  46. if (handle != NULL) {

  47. dlclose(handle);

  48. handle = NULL;

  49. }

  50. } else {

  51. ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",

  52. id, path, *pHmi, handle);

  53. }

  54.  
  55. *pHmi = hmi;

  56.  
  57. return status;

  58. }

1. 首先通过dlopen打开sensors.xxx.so模块,获得其句柄handle

2.调用dlsym去获取结构体hw_module_t结构体的地址,注意这里传入的字符串为HAL_MODULE_INFO_SYM_AS_STR,定义在hardware.h头文件中

 
  1. /**

  2. * Name of the hal_module_info

  3. */

  4. #define HAL_MODULE_INFO_SYM HMI

  5.  
  6. /**

  7. * Name of the hal_module_info as a string

  8. */

  9. #define HAL_MODULE_INFO_SYM_AS_STR "HMI"

这里为什么要去取名字为HMI的地址,我猜想它应该是HAL模块的入口了。

课外知识—ELF文件格式:

ELF = Executable and Linkable Format,可执行连接格式,是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的,扩展名为elf。一个ELF头在文件的开始,保存了路线图(road map),描述了该文件的组织情况。sections保存着object 文件的信息,从连接角度看:包括指令,数据,符号表,重定位信息等等。通过file命令我们可知sensors.xx.so是一个ELF文件格式

 
  1. tiny.hui@build-server:~$ file sensors.msm8952.so

  2. sensors.msm8952.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), BuildID[md5/uuid]=0x25812b01ab4700281b41f61327075611, not stripped

因此,通过linux的readelf命令我们可以查看该文件的内部布局及符号表等信息。

 
  1. tiny.hui@build-server:~$ readelf -s sensors.msm8952.so

  2.  
  3. Symbol table '.dynsym' contains 157 entries:

  4. Num: Value Size Type Bind Vis Ndx Name

  5. 0: 00000000 0 NOTYPE LOCAL DEFAULT UND

  6. 1: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_finalize@LIBC (2)

  7. 2: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit@LIBC (2)

  8. 3: 00000000 0 FUNC GLOBAL DEFAULT UND __register_atfork@LIBC (2)

  9. 4: 00000000 0 FUNC GLOBAL DEFAULT UND pthread_mutex_lock@LIBC (2)

  10.         …………………………// 省略无关信息

  11. 108: 00006d5c 16 FUNC WEAK DEFAULT 13 __aeabi_ldiv0

  12. 109: 000042d5 14 FUNC WEAK DEFAULT 13 _ZNSt3__13mapIi10FullHand

  13. 110: 000053d7 12 FUNC WEAK DEFAULT 13 _ZTv0_n12_NSt3__114basic_

  14. 111: 0000a0cd 0 NOTYPE GLOBAL DEFAULT ABS _end

  15. 112: 000054b1 40 FUNC GLOBAL DEFAULT 13 _ZN16SensorEventQueue7deq

  16. 113: 0000a00c 136 OBJECT GLOBAL DEFAULT 23 HMI

  17. 114: 000053eb 52 FUNC GLOBAL DEFAULT 13 _ZN16SensorEventQueueC1Ei

  18. 115: 00006d5c 16 FUNC WEAK DEFAULT 13 __aeabi_idiv0

  19. 116: 00003879 14 FUNC WEAK DEFAULT 13 _ZNSt3__115basic_streambu

  20. 117: 00003c75 76 FUNC WEAK DEFAULT 13 _ZNSt3__113basic_filebufI

  21. 118: 0000a098 12 OBJECT GLOBAL DEFAULT 24 full_to_global

由符号表可知,HMI的地址为000a00c,拿到函数地址,当然就可以执行对应的代码了。

QualComm Sensor HAL

因此我们接着看sensor_hal层,高通的Sensor实现了自己的HAL,其源码在vendor/qcom/proprietary/sensors/dsps/libhalsensors路径下,通过Android.mk我们也可以确定他确实是我们前面load方法打开的动态链接库,其编译后会生成sensor.msm8952.so:

 
  1. # vendor/qcom/proprietary/sensors/dsps/libhalsensors/Android.mk

  2. ifeq ($(USE_SENSOR_MULTI_HAL),true)

  3. LOCAL_MODULE := sensors.ssc

  4. LOCAL_CLANG := false

  5. else

  6. LOCAL_MODULE := sensors.$(TARGET_BOARD_PLATFORM)

  7. LOCAL_MODULE_RELATIVE_PATH := hw

  8. endif

那么HMI的入口到底定义在这里的那个文件中呢? 

功夫不负有心人,在sensors_hal.cpp中,我们终于找到了HMI的入口,即下面的结构体:

 
  1. static struct hw_module_methods_t sensors_module_methods = {

  2. .open = sensors_open

  3. };

  4.  
  5. struct sensors_module_t HAL_MODULE_INFO_SYM = {

  6. .common = {

  7. .tag = HARDWARE_MODULE_TAG,

  8. .module_api_version = (uint16_t)SENSORS_DEVICE_API_VERSION_1_3,

  9. .hal_api_version = HARDWARE_HAL_API_VERSION,

  10. .id = SENSORS_HARDWARE_MODULE_ID,

  11. .name = "QTI Sensors Module",

  12. .author = "Qualcomm Technologies, Inc.",

  13. .methods = &sensors_module_methods,

  14. .dso = NULL,

  15. .reserved = {0},

  16. },

  17. .get_sensors_list = sensors_get_sensors_list,

  18. .set_operation_mode = sensors_set_operation_mode

  19. };

HAL_MODULE_INFO_SYM即上文提到的HMI变量,恭喜各位,这里我们就开启了QualComm Sensor HAL的大门。

最后这个hw_module_t的结构体句柄会返回给我们的SensorDevice的构造函数里:

 
  1. SensorDevice::SensorDevice()

  2. : mSensorDevice(0),

  3. mSensorModule(0)

  4. {

  5. status_t err = hw_get_module(SENSORS_HARDWARE_MODULE_ID,

  6. (hw_module_t const**)&mSensorModule);

  7.  
  8. ALOGE_IF(err, "couldn't load %s module (%s)",

  9. SENSORS_HARDWARE_MODULE_ID, strerror(-err));

  10.  
  11. if (mSensorModule) {

  12. err = sensors_open_1(&mSensorModule->common, &mSensorDevice);

接着,通过sensors_open_1方法将module->common传入,打开我们的sensor驱动。

 
  1. // hardware/libhardware/include/hardware/sensors.h

  2. static inline int sensors_open_1(const struct hw_module_t* module,

  3. sensors_poll_device_1_t** device) {

  4. return module->methods->open(module,

  5. SENSORS_HARDWARE_POLL, (struct hw_device_t**)device);

  6. }

  7.  
  8. static inline int sensors_close_1(sensors_poll_device_1_t* device) {

  9. return device->common.close(&device->common);

  10. }

回过头去看看HMI的结构体定义,其中module->common->open被赋值为sensors_module_methods,其只有一个open方法,因此,module->methods->open最终会调用sensors_open方法来打开驱动程序。

到这里native到hal层的逻辑其实已经基本上分析完了。

总结

通过hw_get_module去加载我们的HAL层实现库,并且得到入口函数句柄,拿着这个句柄就可以继而操作HAL层提供的方法了,通过上面的分析,相信大家已经对这部分已经有了一个更清晰的认识,其他hal层的业务逻辑大抵如此,当你看到hw_get_module,你就该知道去哪里看接下来的逻辑了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值