Android 经典hal开发实战-千里马android framework车载车机手机系统开发

背景

hi,粉丝朋友们:
大家好!今天来一个经典hal的实现,首先说一下哈,经典hal在正常在Treble计划后就不再建议使用了,高版本一般都是hidl,aidl。目前新版本一般都是强制使用aidl,那为啥还需要讲解这个经典hal呢?
1、虽然采用了treble计划后,system/vendor隔离后,但是因为经典hal是个so,而且代码一般不需要强依赖so,具有一个非常好的移植性,导致虽然变成了google要求的hidl,其实内部依然实现是经典hal的so
2、经典hal so在trable计划以前已经经历了很多个版本,业务执行的稳定性很好,基本上厂商没啥动力推倒重来,基于新的hidl,aidl再写一遍hal业务

总结:treble计划虽然系统已经大部分都是hidl,aidl实现方式,但是这个只是一个对外接口层面的,内部实现的话其实完全可以继续调用以前写好的经典hal,这个方式保证了稳定性,修改量小
在这里插入图片描述
所以基于以上背景,掌握经典hal的相关知识完全是有必要的,不然就可能会导致你根本看不懂很多hal实现

HAL module的架构分析

注意这里只进行依赖libhardware库这种方式实现,不讲解legacy方式

HAL module架构主要分为以下3个结构体:

struct hw_module_t
struct hw_module_methods_t
struct hw_device_t

3个结构体是写这个标准方式hal的最关键部分,它们定义是在
hardware/libhardware/include/hardware/hardware.h
在这里插入图片描述

下面3个进行详细介绍其功能和作用

hw_module_t部分

相关的定义代码如下:


/**
 * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
 * and the fields of this data structure must begin with hw_module_t
 * followed by module specific information.
 */
typedef struct hw_module_t {
    /** tag must be initialized to HARDWARE_MODULE_TAG */
    uint32_t tag;

    /**
     * The API version of the implemented module. The module owner is
     * responsible for updating the version when a module interface has
     * changed.
     *
     * The derived modules such as gralloc and audio own and manage this field.
     * The module user must interpret the version field to decide whether or
     * not to inter-operate with the supplied module implementation.
     * For example, SurfaceFlinger is responsible for making sure that
     * it knows how to manage different versions of the gralloc-module API,
     * and AudioFlinger must know how to do the same for audio-module API.
     *
     * The module API version should include a major and a minor component.
     * For example, version 1.0 could be represented as 0x0100. This format
     * implies that versions 0x0100-0x01ff are all API-compatible.
     *
     * In the future, libhardware will expose a hw_get_module_version()
     * (or equivalent) function that will take minimum/maximum supported
     * versions as arguments and would be able to reject modules with
     * versions outside of the supplied range.
     */
    uint16_t module_api_version;
#define version_major module_api_version
    /**
     * version_major/version_minor defines are supplied here for temporary
     * source code compatibility. They will be removed in the next version.
     * ALL clients must convert to the new version format.
     */

    /**
     * The API version of the HAL module interface. This is meant to
     * version the hw_module_t, hw_module_methods_t, and hw_device_t
     * structures and definitions.
     *
     * The HAL interface owns this field. Module users/implementations
     * must NOT rely on this value for version information.
     *
     * Presently, 0 is the only valid value.
     */
    uint16_t hal_api_version;
#define version_minor hal_api_version

    /** Identifier of module */
    const char *id;

    /** Name of this module */
    const char *name;

    /** Author/owner/implementor of the module */
    const char *author;

    /** Modules methods */
    struct hw_module_methods_t* methods;

    /** module's dso */
    void* dso;

#ifdef __LP64__
    uint64_t reserved[32-7];
#else
    /** padding to 128 bytes, reserved for future use */
    uint32_t reserved[32-7];
#endif

} hw_module_t;

代码很少,注释很多,不过都是英文的,这里给大家总结一下,即常用的成员变量:

1、每一个hardware硬件抽象模块必须定义有一个模块结构体,名字是HAL_MODULE_INFO_SYM,即HMI,这个模块结构体第一个成员变量类型必须是hw_module_t
2、hw_module_t的tag的值必须为HARDWARE_MODULE_TAG,这个就是来标识是硬件抽象的结构体
3、id这个属性代表这个module的唯一性,即每个硬件抽象模块都会有自己的id
4、methods这个代表每个硬件抽象模块对应的方法结构体,类型hw_module_methods_t

常见定义和实现方式如下:

定义结构体:

#define INVCASE_HARDWARE_MODULE_ID  "mytest"  
struct mytest_module_t {
    struct hw_module_t common;
};

实例化HAL_MODULE_INFO_SYM:

/* every module must have HAL_MODULE_INFO_SYM */
struct mytest_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .version_major = 1,
        .version_minor = 0,
        .id = MYTEST_HARDWARE_MODULE_ID,
        .name = MYTEST_HARDWARE_MODULE_ID,
        .author = "test",
        .methods = &mytest_module_methods,
    },
};

hw_module_methods_t部分

typedef struct hw_module_methods_t {
    /** Open a specific device */
    int (*open)(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device);

} hw_module_methods_t;

在结构体 hw_module_methods_t 中只有一个成员,它是一个函数指针,名字是open,它主要作用就是用来打开硬件抽象层中给的硬件设备。因为一个硬件抽象模块module可以包含多个设备device,所以这里需要指定id。

moudlue:代表打开硬件设备设备所属模块
id:代表打开设备的id
device:这个双重指针,一般是输出内容到这个参数里面,即返回值,用来描述一个打开的硬件设备

hw_device_t部分


/**
 * Every device data structure must begin with hw_device_t
 * followed by module specific public methods and attributes.
 */
typedef struct hw_device_t {
    /** tag must be initialized to HARDWARE_DEVICE_TAG */
    uint32_t tag;

    /**
     * Version of the module-specific device API. This value is used by
     * the derived-module user to manage different device implementations.
     *
     * The module user is responsible for checking the module_api_version
     * and device version fields to ensure that the user is capable of
     * communicating with the specific module implementation.
     *
     * One module can support multiple devices with different versions. This
     * can be useful when a device interface changes in an incompatible way
     * but it is still necessary to support older implementations at the same
     * time. One such example is the Camera 2.0 API.
     *
     * This field is interpreted by the module user and is ignored by the
     * HAL interface itself.
     */
    uint32_t version;

    /** reference to the module this device belongs to */
    struct hw_module_t* module;

    /** padding reserved for future use */
#ifdef __LP64__
    uint64_t reserved[12];
#else
    uint32_t reserved[12];
#endif

    /** Close this device */
    int (*close)(struct hw_device_t* device);

} hw_device_t;

其实这个和前面的hw_module_t高度相识,只不过一个代表整个模块一个代表模块的设备
同样总结一下hw_device_t:
1、每个硬件设备都必须自定义一个硬件设备的结构体,第一个成员变量为hw_device_t
2、tag必须为所在的硬件模块HARDWARE_DEVICE_TAG,代表他是个硬件设备结构体
3、会有一个close的函数指针,主要是关闭一个硬件设备
4、自定义设备结构体,除了第一个成员hw_device_t外,最重要就是会定义其他成员方法指针,这些方法就是用来对外提供操作硬件设备的

常见自定义的hw_device_t如下:

struct mytest_device_t {
    struct hw_device_t common; //第一成员必须为hw_device_t
   	int(*addTest)(int a,int b);
};

构造结构过程:

 // use calloc to initialize memory with 0
 struct mytest_device_t * device = NULL;
    device = ( struct mytest_device_t * )malloc(sizeof(*device));
    memset(device,0,sizeof(*device));
    device->common.tag = HARDWARE_DEVICE_TAG;
    device->common.version = 0;
    device->common.module = (hw_module_t*)module;
    device->common.close = close_mytest;
    device->addTest = addTest;

上面最重要就是把hw_device_t构造出,而且给成员变量test赋值了具体方法,后面获取了这个hw_device_t就可以调用真正的hal实现方法了

HAL module的使用步骤

上面只是一个个的定义好硬件抽象模块的步骤,接下来是要要怎么使用上面的hardware接口进行调用到相关的hal方法呢?
1、通过hw_get_module(char* id, struct hw_module_t ** module) 方法获取一个硬件抽象模块的指针

2、通过模块调用module->common.methods->open()方法获取一个硬件抽象即hw_device_t的指针

3、使用硬件抽象设备的hw_device_t指针调用具体的hal实现方法,比如上面 mytest_dev->addTest

相关调用hal的代码如下:

 mytest_module_t* module;
    if (hw_get_module(MYTEST_HARDWARE_MODULE_ID,(const hw_module_t**)&module) == 0) { //获取hw_module_t
         //调用methods的open方法,目的获取hw_device_t的指针
        if (((hw_module_t*)module)->methods->open((hw_module_t*)module,MYTEST_HARDWARE_MODULE_ID,(struct hw_device_t**)&mytest_device) == 0) { //获取hw_device_t对象指针
               //利用获取的mytest_device,调用对于的方法
        int result = mytest_device->addTest(1,2);
        }
    }


hardware.c源码分析

上面使用的hw_get_module这些方法到底在哪里实现的呢?
其实这些代码的实现都是在hardware.c文件中
下面从hw_get_module方法作为切入点全面展开分析一下相关的
hardware/libhardware/hardware.c

int hw_get_module(const char *id, const struct hw_module_t **module)
{
//调用到了hw_get_module_by_class,方法第二参数是NULL
    return hw_get_module_by_class(id, NULL, module);
}

int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module)
{
    int i = 0;
    char prop[PATH_MAX] = {0};
    char path[PATH_MAX] = {0};
    char name[PATH_MAX] = {0};
    char prop_name[PATH_MAX] = {0};


    if (inst)
        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
    else
        strlcpy(name, class_id, PATH_MAX);//把相关的id赋值给了name
	//第一优先级遍历看看是否存在ro.hardware.mytest 的prop
    snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
    if (property_get(prop_name, prop, NULL) > 0) { //如果找到了这个ro.hardware.mytest 的prop
        if (hw_module_exists(path, sizeof(path), name, prop) == 0) { //根据prop值调用hw_module_exists看看是否hw存在
            goto found;
        }
    }

	//直接module的prop没有找到,则开启第二优先级遍历,这个主要便利设备相关属性,比如  "ro.hardware",

    /* Loop through the configuration variants looking for a module */
    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
        if (property_get(variant_keys[i], prop, NULL) == 0) {
            continue;
        }
        if (hw_module_exists(path, sizeof(path), name, prop) == 0) {//然后去看看是否哪个prop的值有对应的so的
            goto found;
        }
    }
  //上面都没有找到,只能使用一个默认的hal so进行
    /* Nothing found, try the default */
    if (hw_module_exists(path, sizeof(path), name, "default") == 0) { 
        goto found;
    }

    return -ENOENT;
//开始处理找到so相关的业务
found:
    /* load the module, if this fails, we're doomed, and we should not try
     * to load a different variant. */
    return load(class_id, path, module);//传递找到的so路径然后进行so的加载
}

hw_module_exists会根据传递进来的name,subname,然后去拼出一个so的文件名字,看看这个so文件是否存在,存在这返回找到

/*
 * Check if a HAL with given name and subname exists, if so return 0, otherwise
 * otherwise return negative.  On success path will contain the path to the HAL.
 */
static int hw_module_exists(char *path, size_t path_len, const char *name,
                            const char *subname)
{
//HAL_LIBRARY_PATH3 "/odm/lib64/hw" 最先看看odm是否有这个so
    snprintf(path, path_len, "%s/%s.%s.so",
             HAL_LIBRARY_PATH3, name, subname);
    if (path_in_path(path, HAL_LIBRARY_PATH3) && access(path, R_OK) == 0)
        return 0;
//HAL_LIBRARY_PATH2 "/vendor/lib64/hw“接下来是vendor下面是否有

    snprintf(path, path_len, "%s/%s.%s.so",
             HAL_LIBRARY_PATH2, name, subname);
    if (path_in_path(path, HAL_LIBRARY_PATH2) && access(path, R_OK) == 0)
        return 0;

#ifndef __ANDROID_VNDK__
    snprintf(path, path_len, "%s/%s.%s.so",
             HAL_LIBRARY_PATH1, name, subname);
    if (path_in_path(path, HAL_LIBRARY_PATH1) && access(path, R_OK) == 0)
        return 0;
#endif

    return -ENOENT;
}

上面可以看出hw_module_exists会去几个路径找,优先级是oem > vendor> system

如果prop指定的都没有找到最后只能用最后的default看看是否存在,如果这个default so都没有,那么就会报错了,即无法获取到对于的硬件抽象模块,说明硬件抽象这部分功能就无法使用了。

一般情况下都最少可以找到一个default的so,那么找到后会执行上面的load方法,把这个so进行相关load

这个load方法是最关键的,来看看系统到底是怎么实现的:



static int load(const char *id,
        const char *path,
        const struct hw_module_t **pHmi)
{
  //省略部分
    /* Get the address of the struct hal_module_info. */
    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;//就是靠这个固定HMI字符去so中找符号
    hmi = (struct hw_module_t *)dlsym(handle, sym);//这里是关键传递返回HMI的地址,也就相当于找到了这个结构体指针hmi
 
    *pHmi = hmi;//把找到

    return status;
}

补充介绍一下两个函数:
dlopen()是一个强大的库函数。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。
dlsym是一个计算机函数,功能是根据动态链接库操作句柄与符号,返回符号对应的地址,不但可以获取函数地址,也可以获取变量地址。

上面的load方法就是我们说的hardware动态性的关键,它是在运行时候才真正依赖具体硬件抽象so,编译期间只需要依赖公共的hardware相关公共类既可以,不需要so,所以这里就给硬件抽象提供商带来很大灵活性,哪怕硬件厂商不提供也一般会有一个default的so保证不会产生什么严重的崩溃和强依赖问题,大大减低了aosp的代码对于硬件抽象的各个厂商的耦合性。

这里看看手机上的相关hal so:

在这里插入图片描述
这里在看看哪个prop是ms8998
在这里插入图片描述
本文章对应视频手把手教你学framework:
https://mp.weixin.qq.com/s/LbVLnu1udqExHVKxd74ILg
私聊作者+v(androidframework007)
点击这里 https://mp.weixin.qq.com/s/Qv8zjgQ0CkalKmvi8tMGaw
视频:https://www.bilibili.com/video/BV1wc41117L4/
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值