Android硬件抽象Hardware库加载过程源码分析

本文介绍了Android系统的硬件抽象层(HAL)接口实现原理。详细分析了如何通过hal_get_module函数定位并加载特定硬件模块的动态库,以及如何验证模块的有效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作为开放而非开源的Android系统,由于其基于Linux内核实现,在不违背Linux基于GPL许可前提下,为了隐藏各厂家自身特定硬件驱动实现细节,在用户空间定义了一套硬件抽象层,对硬件的操作细节从内核空间转移到用户空间。各厂商在Android的硬件抽象层实现特定硬件的操作细节,并编译成动态库,以库的形式提供给用户使用。Linux内核是基于GPL许可,即对源码的修改都必须开源,而Android是基于ASL许可,即可以随意使用源码,无需开源,因此将原本应该位于Linux内核的硬件驱动逻辑转移到Android平台来,就可以不必开源,从而保护了厂家的利益。Android的硬件抽象层就是为了保障Android平台基于Linux开发的硬件驱动和应用程序不必遵循GPL许可而保持封闭。因此Android就提供了一套访问硬件抽象层动态库的接口,各厂商只需要为他们的硬件实现软件操作细节。Android系统编译的硬件抽象库存放于/system/lib/hw目录下,如下图所示:


本文简单介绍Android系统提供的硬件抽象库访问接口。

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. }  
int hw_get_module(const char *id, const struct hw_module_t **module)
{
    return hw_get_module_by_class(id, NULL, module);
}
参数id为硬件抽象层模块ID,hw_module_t是硬件抽象层模块描述结构。

  1. static const char *variant_keys[] = {  
  2.     "ro.hardware",  /* This goes first so that it can pick up a different file on the emulator. */  
  3.     "ro.product.board",  
  4.     "ro.board.platform",  
  5.     "ro.arch"  
  6. };  
  7. static const int HAL_VARIANT_KEYS_COUNT =  
  8.     (sizeof(variant_keys)/sizeof(variant_keys[0]));  
  9.       
  10. int hw_get_module_by_class(const char *class_id, const char *inst,  
  11.                            const struct hw_module_t **module)  
  12. {  
  13.     int status;  
  14.     int i;  
  15.     const struct hw_module_t *hmi = NULL;  
  16.     char prop[PATH_MAX];//属性   
  17.     char path[PATH_MAX];//路径   
  18.     char name[PATH_MAX];//名称   
  19.     if (inst)  
  20.         snprintf(name, PATH_MAX, "%s.%s", class_id, inst);  
  21.     else  
  22.         strlcpy(name, class_id, PATH_MAX);  
  23.     //遍历数组variant_keys   
  24.     //HAL_VARIANT_KEYS_COUNT =(sizeof(variant_keys)/sizeof(variant_keys[0]));   
  25.     for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {  
  26.         if (i < HAL_VARIANT_KEYS_COUNT) {  
  27.             if (property_get(variant_keys[i], prop, NULL) == 0) {  
  28.                 continue;  
  29.             }  
  30.             snprintf(path, sizeof(path), "%s/%s.%s.so",HAL_LIBRARY_PATH2, name, prop);  
  31.             if (access(path, R_OK) == 0) break;  
  32.             snprintf(path, sizeof(path), "%s/%s.%s.so",HAL_LIBRARY_PATH1, name, prop);  
  33.             if (access(path, R_OK) == 0) break;  
  34.         } else {  
  35.             snprintf(path, sizeof(path), "%s/%s.default.so",HAL_LIBRARY_PATH1, name);  
  36.             if (access(path, R_OK) == 0) break;  
  37.         }  
  38.     }  
  39.     status = -ENOENT;  
  40.     if (i < HAL_VARIANT_KEYS_COUNT+1) {  
  41.         //加载动态库   
  42.         status = load(class_id, path, module);  
  43.     }  
  44.     return status;  
  45. }  
static const char *variant_keys[] = {
    "ro.hardware",  /* This goes first so that it can pick up a different file on the emulator. */
    "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_by_class(const char *class_id, const char *inst,
                           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];//路径
    char name[PATH_MAX];//名称
    if (inst)
        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
    else
        strlcpy(name, class_id, PATH_MAX);
	//遍历数组variant_keys
	//HAL_VARIANT_KEYS_COUNT =(sizeof(variant_keys)/sizeof(variant_keys[0]));
    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);
            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;
        }
    }
    status = -ENOENT;
    if (i < HAL_VARIANT_KEYS_COUNT+1) {
		//加载动态库
        status = load(class_id, path, module);
    }
    return status;
}
函数首先遍历数组variant_keys,读取以数组元素为属性的值,即循环读取
"ro.hardware" 
"ro.product.board"
"ro.board.platform"
"ro.arch"
这四个属性,如果其中有一个属性设置了值的话,将硬件抽象层库的路径,名称,读取的属性值格式化到path字符串中。
path=/vendor/lib/hw/gralloc.[prop].so
然后判断该路径下的so库能否访问,如果不行,则判断/system/lib/hw目录下对应的模块so能否访问,
path=/system/lib/hw/gralloc.[prop].so
即函数hw_get_module依次在目录/system/lib/hw和/vendor/lib/hw中查找一个名称为"<MODULE_ID>.variant.so"的文件,其中,<MODULE_ID>是一个模块ID,而variant表示"ro.hardware"、"ro.product.board"、"ro.board.platform"和"ro.arch"四个系统属性值之一。只要其中的一个文件存在,  函数hw_get_module就会停止查找过程,如果在/system/lib/hw和/vendor/lib/hw中均不存这些文件,那么就会在目录/system/lib/hw中查找是否存在一个名称为<MODULE_ID>.default.so的文件,并调用load函数来加载so库,得到该硬件抽象层模块的模块描述符hw_module_t

  1. static int load(const char *id,const char *path,const struct hw_module_t **pHmi)  
  2. {  
  3.     int status;  
  4.     void *handle;  
  5.     struct hw_module_t *hmi;  
  6.     //打开so动态库   
  7.     handle = dlopen(path, RTLD_NOW);  
  8.     if (handle == NULL) {  
  9.         char const *err_str = dlerror();  
  10.         ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");  
  11.         status = -EINVAL;  
  12.         goto done;  
  13.     }  
  14.     //得到模块描述符的首地址,在模块注册时,将模块描述符命名为HAL_MODULE_INFO_SYM,其真实名称为HMI,   
  15.     //这里就是取得结构体HMI的首地址,#define HAL_MODULE_INFO_SYM_AS_STR  "HMI"   
  16.     const char *sym = HAL_MODULE_INFO_SYM_AS_STR;  
  17.     hmi = (struct hw_module_t *)dlsym(handle, sym);  
  18.     if (hmi == NULL) {  
  19.         ALOGE("load: couldn't find symbol %s", sym);  
  20.         status = -EINVAL;  
  21.         goto done;  
  22.     }  
  23.     /* 匹配模块ID */  
  24.     if (strcmp(id, hmi->id) != 0) {  
  25.         ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);  
  26.         status = -EINVAL;  
  27.         goto done;  
  28.     }  
  29.     //将模块句柄保存到hw_module_t的成员变量dso中   
  30.     hmi->dso = handle;  
  31.     status = 0;  
  32.     done:  
  33.     if (status != 0) {  
  34.         hmi = NULL;  
  35.         if (handle != NULL) {  
  36.             dlclose(handle);  
  37.             handle = NULL;  
  38.         }  
  39.     } else {  
  40.         ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",id, path, *pHmi, handle);  
  41.     }  
  42.     *pHmi = hmi;//返回模块描述符hw_module_t的地址   
  43.     return status;  
  44. }  
static int load(const char *id,const char *path,const struct hw_module_t **pHmi)
{
    int status;
    void *handle;
    struct hw_module_t *hmi;
	//打开so动态库
    handle = dlopen(path, RTLD_NOW);
    if (handle == NULL) {
        char const *err_str = dlerror();
        ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
        status = -EINVAL;
        goto done;
    }
	//得到模块描述符的首地址,在模块注册时,将模块描述符命名为HAL_MODULE_INFO_SYM,其真实名称为HMI,
	//这里就是取得结构体HMI的首地址,#define HAL_MODULE_INFO_SYM_AS_STR  "HMI"
    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
    hmi = (struct hw_module_t *)dlsym(handle, sym);
    if (hmi == NULL) {
        ALOGE("load: couldn't find symbol %s", sym);
        status = -EINVAL;
        goto done;
    }
    /* 匹配模块ID */
    if (strcmp(id, hmi->id) != 0) {
        ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
        status = -EINVAL;
        goto done;
    }
	//将模块句柄保存到hw_module_t的成员变量dso中
    hmi->dso = handle;
    status = 0;
    done:
    if (status != 0) {
        hmi = NULL;
        if (handle != NULL) {
            dlclose(handle);
            handle = NULL;
        }
    } else {
        ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",id, path, *pHmi, handle);
    }
    *pHmi = hmi;//返回模块描述符hw_module_t的地址
    return status;
}

硬件抽象层模块编写规范规定每一个硬件抽象层模块都必须导出一个符号名称为HAL_MODULE_INFO_SYM_AS_STR的符号,而且这个符号必须是用来描述一个类型为hw_module_t的结构体的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值