服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

Android Q 打通应用层到 HAL 层--( HAL 模块实现)

日期: 来源:字节流动收集编辑:DJLZPP

从这篇文章开始准备研究应用层到HAL层的一整套流程,目标是写一个APP调用HAL的一个函数,在AOSP源码环境下进行开发,大概流程是: 
APP---->Framework service---->native----->HAL

什么是HAL

HAL全称Hardware Abstract Layer,硬件抽象层,它向下屏蔽了硬件的实现细节,向上提供了抽象接口,HAL是底层硬件和上层框架直接的接口,框架层通过HAL可以操作硬件设备,HAL的实现在用户空间

为什么需要HAL

我们知道Android是基于Linux进行开发的,传统的Linux对硬件的操作基本上都是在内核中,而Android把对硬件的操作分为了两部分,HAL和内核驱动,HAL实现在用户空间,驱动在内核空间,这是因为Linux内核中的代码是需要开源的。

如果把对硬件的操作放在内核这会损害硬件厂商的利益,因为这是别人厂商的商业机密,而现在有了HAL层位于用户空间,硬件厂商就可以将自己的核心算法之类的放在HAL层,保护了自己的利益,这样Android系统才能得到更多厂商的支持

HAL实现的一般规则

每种硬件都对应了一个HAL模块,要向实现自己的HAL,必须满足HAL的相关规则,规则定义在源码hardward目录下,头文件hardward.h,C文件hardward.c  
hardward.h中定义了三个重要的结构体:

struct hw_module_t;
struct hw_module_methods_t;
struct hw_device_t;

其实HAL的实现使用了C中结构体继承的技巧,当然这种继承并不是真正意义的继承,而是一种结构体强制转换,我们可以理解为继承

结构体hw_module_t代表HAL模块,自己定义的HAL模块必须包含一个自定义struct,且必须”继承” hw_module_t(即第一个变量必须为 hw_module_t),且模块的tag必须指定为HARDWARE_MODULE_TAG,代表这是HAL模块的结构体

结构体hw_module_methods_t代表模块的操作方法列表,它内部只有一个函数指针open,用来打开该模块下的设备

结构体hw_device_t代表该模块下的设备,自己定义的HAL模块必须包含一个结构体,且必须“继承” hw_device_t(即第一个变量必须为hw_device_t)

每个自定义HAL模块还有一个模块名和N个设备名(标识模块下的设备个数,一个模块可以有多个设备),

最后这个模块定义好之后还必须导出符号HAL_MODULE_INFO_SYM,指向这个模块,HAL_MODULE_INFO_SYM定义在hardware.h中值为“HMI”

接下来就手动实现一个HAL模块,这个HAL提供一个加法函数  
首先在hardware/libhardware/include/hardware目录下创建hello.h文件

#include <sys/cdefs.h>
#include <sys/types.h>
#include <hardware/hardware.h>
//HAL模块名
#define HELLO_HARDWARE_MODULE_ID "hello"
//HAL版本号
#define HELLO_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(0, 1)
//设备名
#define HARDWARE_HELLO "hello"

//自定义HAL模块结构体
typedef struct hello_module {
    struct hw_module_t common;
} hello_module_t;

//自定义HAL设备结构体
typedef struct hello_device {
    struct hw_device_t common;
    //加法函数
    int (*additionTest)(const struct hello_device *dev,int a,int b,int* total);
} hello_device_t;

//给外部调用提供打开设备的函数
static inline int _hello_open(const struct hw_module_t *module,
        hello_device_t **device) {
    return module->methods->open(module, HARDWARE_HELLO,
            (struct hw_device_t **) device);
}

我们可以看到在hello.h中自定义了两个结构体,分别继承hw_module_t和hw_device_t且在第一个变量,满足前面说的规则,另外定义在device结构体中的函数的第一个参数也必须是device结构体

接着在hardware/libhardware/modules/目录下创建一个hello的文件夹,里面包含一个hello.c和Android.bp,我们来看hello.c文件

#define LOG_TAG "HelloHal"

#include <malloc.h>
#include <stdint.h>
#include <string.h>

#include <log/log.h>

#include <hardware/hello.h>
#include <hardware/hardware.h>

//加法函数实现
static int additionTest(const struct hello_device *dev,int a,int b,int *total)
{
    if(!dev){
        return -1;
    }
    *total = a + b;
    return 0;
}

//关闭设备函数
static int hello_close(hw_device_t *dev)
{
    if (dev) {
        free(dev);
        return 0;
    } else {
        return -1;
    }
}
//打开设备函数
static int hello_open(const hw_module_t* module,const char __unused *id,
                            hw_device_t** device)
{
    if (device == NULL) {
        ALOGE("NULL device on open");
        return -1;
    }

    hello_device_t *dev = malloc(sizeof(hello_device_t));
    memset(dev, 0, sizeof(hello_device_t));

    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = HELLO_MODULE_API_VERSION_1_0;
    dev->common.module = (struct hw_module_t*) module;
    dev->common.close = hello_close;
    dev->additionTest = additionTest;

    *device = &(dev->common);
    return 0;
}

static struct hw_module_methods_t hello_module_methods = {
    .open = hello_open,
};
//导出符号HAL_MODULE_INFO_SYM,指向自定义模块
hello_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag                = HARDWARE_MODULE_TAG,
        .module_api_version = HELLO_MODULE_API_VERSION_1_0,
        .hal_api_version    = HARDWARE_HAL_API_VERSION,
        .id                 = HELLO_HARDWARE_MODULE_ID,
        .name               = "Demo Hello HAL",
        .author             = "dongjiao.tang@tcl.com",
        .methods            = &hello_module_methods,
    },
};

前面我们说过,要想自定义的HAL模块被外部使用需要导出符号HAL_MODULE_INFO_SYM,导出的这个模块结构体最重要的其实就是tag,id和methods,tag必须定义为HARDWARE_MODULE_TAG,id就是在hello.h中定义的模块名。

methods指向hello_module_methods地址,hello_module_methods指向结构体hw_module_methods_t,它里面的open函数指针作用是打开模块下的设备,它指向hello_open函数

hello_open

static int hello_open(const hw_module_t* module,const char __unused *id,
                            hw_device_t** device)
{
    if (device == NULL) {
        ALOGE("NULL device on open");
        return -1;
    }
    hello_device_t *dev = malloc(sizeof(hello_device_t));
    memset(dev, 0, sizeof(hello_device_t));

    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = HELLO_MODULE_API_VERSION_1_0;
    dev->common.module = (struct hw_module_t*) module;
    dev->common.close = hello_close;
    dev->additionTest = additionTest;

    *device = &(dev->common);
    return 0;
}

其实hello_open函数也很简单,就是创建一个hello_device_t结构体,这是我们自定义HAL模块下的设备结构体,然后给这个结构体赋值,我们定义的加法函数additionTest赋值给设备结构体的函数指针,外部就可以使用。

因为这个HAL模块导出了符号HAL_MODULE_INFO_SYM,所以我们能通过模块id找到这个HAL模块,有了模块之后就可以调用它的hello_open函数打开设备,hello_open接收三个参数,module代表这个HAL模块,id代表要打开的是这个模块下哪一个设备,最后一个参数是一个二级指针,其实是我们自定义设备结构体指针的地址,传个地址过来就可以给这个设备结构体赋值。

这样最简单的一个HAL模块就定义好了,它仅仅提供了一个加法函数,对于Android.bp如下:最终编译成一个共享lib库

cc_library_shared {
    name: "hello.default",
    relative_install_path: "hw",
    proprietary: true,
    srcs: ["hello.c"],
    header_libs: ["libhardware_headers"],
    shared_libs: ["liblog"],
}

我们在根目录执行如下命令来编译一下:

mmm hardware/libhardware/modules/hello/

编译成功后生成了一个hello.default.so共享lib库  

hello.default.so

我们将这个so库push进手机/system/lib64路径下,接着需要写一个简单测试程序来测一下

我们在HAL模块目录下再创建一个test目录,test目录下创建一个HelloTest.cpp测试文件和一个Android.bp文件,目录结构如下: 

在这里插入图片描述
HelloTest.cpp源码如下:
#include <hardware/hardware.h>
#include <hardware/hello.h>
#include <log/log.h>
#define TAG "HelloHal"
int main(){
    hello_device_t* hello_device = NULL;

    const hello_module_t * module = NULL;

    int ret = hw_get_module(HELLO_HARDWARE_MODULE_ID,(const struct hw_module_t**)&module);

    if (!ret) {
        ret = _hello_open((const struct hw_module_t*)module,&hello_device);
    }


    if (ret < 0) {
          ALOGD("get hello-hal failed.......");
          return -1;
    }

    int total = 0;
    hello_device->additionTest(hello_device,3,5,&total);
    ALOGD("success start hello-hal....total = %d",total);
    return 0;
}

通过hardware.c提供的hw_get_module函数,传递模块名获取对应的HAL模块,接着调用我们自定义的_hello_open函数通过获取到的模块打开设备,获取到设备之后,我们调用定义在设备结构体中的加法函数

Android.bp定义如下:

cc_binary {
    name: "hello_hal",
    srcs: ["HelloTest.cpp"],
    shared_libs: [
        "liblog",
        "libhardware",
    ],
}

这个test会被编译成二进制可执行文件:hello_hal  

在这里插入图片描述

我们将这个可执行文件push进手机system/bin目录,然后执行  

在这里插入图片描述

可以看到log已经成功打印: 

在这里插入图片描述

到此我们最简单的HAL就已经实现了,这个HAL并不设计底层驱动,实际开发中自己可以在设备结构体中定义函数访问底层硬件。

后续文章会接着写从native层通过HIDL访问HAL,以及framework层通过JNI访问native层,以及通过应用层APK通过AIDL访问framework层,打通应用层到HAL层的整个开发框架。

原文链接: https://blog.csdn.net/qq_34211365/article/details/105492973


相关阅读

  • 干货 | sqlmap 小技巧【文末赠书】

  • 本文仅作为技术讨论及分享,严禁用于任何非法用途。前言在日常使用 sqlmap 中,可能我们会遇到明明手工注入能发现注入点,但是 sqlmap 发两个包就不再往下跑了,这是怎么回事呢?又该
  • 结构化发债再被加码监管!

  • 2023年1月13日,中国证监会修订了《证券期货经营机构私募资产管理业务管理办法》及其配套规范,在修订说明中明确提出要“防堵结构化发债行为”并对私募资管计划的投资比
  • 一作+通讯,Nature Materials!

  • ▲第一作者:Simon A. J. Kimber通讯作者:Simon A. J. Kimber、Simon J. L. Billinge 、Anibal J. Ramirez-Cuesta通讯单位:法国勃艮第大学、美国布鲁克海文国家实验室、美国橡
  • 手把手教你使用 Python 调用 ChatGPT-3.5-API

  • 大家好,我是老表前天 OpenAI 开放了两个新模型的api接口,专门为聊天而生的 gpt-3.5-turbo 和 gpt-3.5-turbo-0301。“ChatGPT is powered by gpt-3.5-turbo, OpenAI’s most a
  • 图解Pandas

  • 来自网络,感觉是机翻,看图就好。。。Pandas 展示请看下表:它描述了一个在线商店的不同产品线,共有四种不同的产品。与前面的例子不同,它可以用NumPy数组或Pandas DataFrame表示

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • Android Q 打通应用层到 HAL 层--( HAL 模块实现)

  • 从这篇文章开始准备研究应用层到HAL层的一整套流程,目标是写一个APP调用HAL的一个函数,在AOSP源码环境下进行开发,大概流程是: APP---->Framework service---->native----->HAL
  • 6问京东百亿补贴

  • 1914年6月28日,塞尔维亚民族主义者加夫里洛·普林西普在萨拉热窝暗杀了弗朗茨·斐迪南大公(奥地利皇储)和他的妻子索菲·霍泰克,史称萨拉热窝事件,它是第一次世界大战的直接导火
  • 【建投专题】初春的寒意,美豆失守1500美分

  • 作者 | 中信建投期货 田亚雄期货投资咨询从业证书号:Z0012209本报告完成时间 | 2023年3月1日重要提示:通过本订阅号发布的观点和信息仅供投资者中符合《证券期货投资者适当性