# Hal 层添加访问 linux 驱动的接口 站点:答源(https://www.dandroid.cn/) 原文:https://www.dandroid.cn/archives/20566 Canonical:https://www.dandroid.cn/archives/20566 作者:admin 发布日期:2023-09-15 最后更新:2023-09-15 17:41 分类:Android嵌入式 / Android系统开发 内容类型:post 说明:本页是原文的纯文本镜像,供大模型与检索器完整读取;不含导航、广告、付费框。引用请以上方「原文」链接为准。 ## 正文 写给应用开发的 Android Framework 教程是一个系列教程,目前已更新以下内容: Android Framework 学习路线指南 系统开发基础 Ubuntu 使用快速入门 Make 构建工具入门 理解 Unicode UTF-8 UTF-16 UTF-32 Linux Shell 脚本编程入门——核心基础语法 SEAndroid 使用极速上手 理解 C++ 的 Memory Order AOSP 上手指南 AOSP 极速上手 系统开发工具推荐 添加 Product 添加 C/C++、Java 可执行程序 添加 C/C++、Java 库 添加配置文件与删除已有模块 系统 App 源码添加 使用 Android Studio 开发系统 App      添加开机自启动 Shell 脚本 学穿 Binder 系列 Binder 基本原理 Binder 程序示例之 C 语言篇 Binder 服务注册过程情景分析之C语言篇 Binder 服务获取与使用过程情景分析之C语言篇 Binder C++ 程序示例 Binder C++ 程序分析之主要类解析 Binder 服务注册过程情景分析之 C++ 篇 Binder 服务获取与使用过程情景分析之 C++ 篇 HAL 与硬件服务 Kernel 下载与编译 Linux 驱动开发入门 Hal 层添加访问 linux 驱动的接口 (本文) 本文基于 AOSP Android10 r41 源码环境 1. Android O 后的 HAL —— Treble 计划 在 Android O 以前,framework 与 hal 是紧耦合的存在于 system.img 中,因此进行版本升级时需要: OEM 厂商适配 framework ,SoC 厂商适配 hal, 之后将修改打包到system.img,生成 OTA 升级包,推送到手机进行 OTA 升级。 ​在 Android O 以后,framework 与 hal 进行了解耦, framework 存在于system.img,hal 存在于 vendor.img ,进行版本升级时,分为两次升级: framework 升级: OEM 厂商适配 framework,将修改打包到system.img, 生成OTA 升级包,推送到手机进行OTA 升级(framework 发生改变,hal 层未变)。 hal 升级:SoC 厂商适配 hal, 将修改打包到 vendor.img, 生成 OTA 升级包,推送到手机进行 OTA 升级(framework发生改变,hal 层发生改变)。 要实现 Android O 以后的升级方式,需要保证 System 分区与 Vendor 分区之间的接口稳定,Android O 中提供了多种技术来维护这个接口的稳定性,其中最主要的是: VNDK:System 分区中的库,这些库 Vendor 分区可能会使用到,这些库由 google 维护和保持其对外的稳定性。 HIDL:Vendor 分区通过 HIDL 方式来对外提供使用接口,需要 SOC 厂商维护其对外接口的稳定性。 接下来我们就来看看 HIDL 在 HAL 层中的使用。 2. Hal 层的实现 源码可以在 https://github.com/yuandaimaahao/AndroidFrameworkTutorial/tree/main/4.Hal%E4%B8%8E%E7%A1%AC%E4%BB%B6%E6%9C%8D%E5%8A%A1/src/HelloHidl_Bindered 这里下载到。 首先我们需要将 Linux 驱动开发入门 中介绍的 hello 驱动添加到内核中,接着在看下面的操作: 2.1 Hal 协议实现 Hal 层的实现一般放在 vendor 目录下,我们在 vendor 目录下创建如下的目录: mkdir -p jelly/hardware/interface/hello_hidl/1.0 接着在 vendor/jelly/hardware/interface/hello_hidl/1.0 目录下创建 Hal 文件; IHello.hal : //定义包名,最后跟一个版本号 package jelly.hardware.hello_hidl@1.0; //定义 hidl 服务对外提供的接口 interface IHello { //for test,generates 后面跟的是返回类型 addition_hidl(uint32_t a,uint32_t b) generates (uint32_t total); //写 hello 驱动 write(string name) generates (uint32_t result); //读 hello 驱动 read() generates (string name); }; 这里的 IHello.hal 定义了我们的服务对外提供了哪些函数。可以认为这就是我们服务的对外协议。协议一般定义好就不会再修改,以保持对外的稳定性。 关于 hal 的写法,可以参考官方的文档,另外也可以参考 hardware 目录下系统自带的 hal 的写法。 2.2 hal 文件生成 C++ 源文件 接着我们使用 hidl-gen 命令将我们写的 hal 文件转换为 C++ 文件: source build/envsetup.sh PACKAGE=jelly.hardware.hello_hidl@1.0 LOC=vendor/jelly/hardware/interfaces/hello_hidl/1.0/default hidl-gen -o $LOC -Lc++-impl -rjelly.hardware:vendor/jelly/hardware/interfaces $PACKAGE 接着就会生成一些 C++ 代码: 在 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下会生成 Hello.cpp 和 Hello.h 在 out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++/gen/jelly/hardware/hello_hidl/1.0 中生成了如下代码: 在 out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++_headers/gen/jelly/hardware/hello_hidl/1.0 中生成了如下代码: 接着修改 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下生成的 Hello.cpp: // FIXME: your file license if you have one #include "Hello.h" #include #include #include #include #include #include namespace jelly { namespace hardware { namespace hello_hidl { namespace V1_0 { namespace implementation { // Methods from ::jelly::hardware::hello_hidl::V1_0::IHello follow. Return Hello::addition_hidl(uint32_t a, uint32_t b) { ALOGE("hello_hidl service is init success....a :%d,b:%d",a,b); return uint32_t {}; return uint32_t {}; } Return Hello::write(const hidl_string& name) { int fd = open("/dev/hello", O_RDWR); if (fd == -1) { printf("can not open file /dev/hello\n"); return uint32_t { 0 }; } ::write(fd, name.c_str(), 100); close(fd); return uint32_t { 1 }; } //通过 read_cb function 回调函数,回传数据 Return Hello::read(read_cb _hidl_cb) { char buf[100]; int fd = open("/dev/hello", O_RDWR); if (fd == -1) { printf("can not open file /dev/hello\n"); return Void(); } ::read(fd, buf, 100); hidl_string result(buf); _hidl_cb(result); close(fd); return Void(); } } // namespace implementation } // namespace V1_0 } // namespace hello_hidl } // namespace hardware } // namespace jelly 这里主要是对我们的协议进行实现,实现了对上一节实现的设备文件 /dev/hello 的读写。至此我们的 hidl 服务就定义好了 2.3 服务端实现 接着我们需要写一个 Server 端来向 HwServiceManager 注册我们的服务。在 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下添加 service.cpp: #include #include #include #include #include "Hello.h" using android::hardware::configureRpcThreadpool; using android::hardware::joinRpcThreadpool; using jelly::hardware::hello_hidl::V1_0::IHello; using jelly::hardware::hello_hidl::V1_0::implementation::Hello; int main() { ALOGD("hello-hidl is starting..."); configureRpcThreadpool(4, true /* callerWillJoin */); android::sp service = new Hello(); android::status_t ret = service->registerAsService(); if (ret != android::NO_ERROR) { } joinRpcThreadpool(); return 0; //Passthrough模式 //return defaultPassthroughServiceImplementation(4); } 我们的服务端需要在开机时启动,创建 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default/jelly.hardware.hello_hidl@1.0-service.rc 文件: service vendor_hello_hidl_service /vendor/bin/hw/jelly.hardware.hello_hidl@1.0-service class hal user system group system 接着我们需要添加 VINTF 对象,对于注册到 hwservicemanager 的服务都需要添加一个 VINTF 对象。对于编码来说 VINTF 对象就是一个 xml 文件,创建 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default/jelly.hardware.hello_hidl@1.0-service.xml 文件: jelly.hardware.hello_hidl hwbinder 1.0 IHello default 2.4 生成 Android.bp 接着我们来生成 Android.bp: hidl-gen -o $LOC -Landroidbp-impl -rjelly.hardware:vendor/jelly/hardware/interfaces $PACKAGE 这个命令会在 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下生成一个 Android.bp,我们在生成的基础上稍作修改如下: // FIXME: your file license if you have one cc_library_shared { name: "jelly.hardware.hello_hidl@1.0-impl", relative_install_path: "hw", proprietary: true, srcs: [ "Hello.cpp", ], shared_libs: [ "libhidlbase", "libhidltransport", "libutils", "jelly.hardware.hello_hidl@1.0", "liblog", ], } cc_binary { name: "jelly.hardware.hello_hidl@1.0-service", init_rc: ["jelly.hardware.hello_hidl@1.0-service.rc"], vintf_fragments: ["jelly.hardware.hello_hidl@1.0-service.xml"], defaults: ["hidl_defaults"], relative_install_path: "hw", vendor: true, srcs: ["service.cpp", "Hello.cpp"], shared_libs: [ "jelly.hardware.hello_hidl@1.0", "libhardware", "libhidlbase", "libhidltransport", "libutils", "liblog", ], } 生成的库里面有一个依赖 jelly.hardware.hello_hidl@1.0,接着我们来生成这个库对应的 Android.bp: 在 hardware/interfaces 目录下将 update-makefiles.sh 拷贝到 vendor/jelly/hardware/interfaces/ 目录下,并修改如下: #!/bin/bash source $ANDROID_BUILD_TOP/system/tools/hidl/update-makefiles-helper.sh do_makefiles_update \ "jelly.hardware:vendor/jelly/hardware/interfaces" 接着执行: ./vendor/jelly/hardware/interfaces/update-makefiles.sh 就会生成 vendor/jelly/hardware/interfaces/Android.bp 文件: hidl_interface { name: "jelly.hardware.hello_hidl@1.0", root: "jelly.hardware", srcs: [ "IHello.hal", ], interfaces: [ "android.hidl.base@1.0", ], gen_java: false, } 其中的 hidl_interface 是 hidl 独有的,当编译源码时,它会将 out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++/gen/jelly/hardware/hello_hidl/1.0 和 out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++_headers/gen/jelly/hardware/hello_hidl/1.0 目录下的源码编译为 jelly.hardware.hello_hidl@1.0.so 文件,并预制到手机的 /vendor/lib 和 /vendor/lib64/ 目录下。 为了使编译通过,新建 vendor/jelly/hardware/interfaces/Android.bp 文件: hidl_package_root { name: "jelly.hardware", path: "vendor/jelly/hardware/interfaces", } 这个 Android.bp 的作用是告诉编译系统包名与路径的映射关系。 接着新建 vendor/jelly/hardware/interfaces/current.txt 文件,current.txt 记录了所有 hal 接口的 hash 值,接口有变化时,同时需要更新 current.txt 中的 hash 值,这是我们先随便设置一个 hash 值: 123456 jelly.hardware.hello_hidl@1.0::IHello 再执行一遍 update-makefiles.sh,这个时候就会发现提示 hash 值不正确了,同时会给出正确的 hash 值,我们把正确的 hash 值替换到 current.txt 即可。 2.5 客户端编写 在 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下创建如下的文件和文件夹: 其中 hello_hidl_test.cpp: #include #include #define LOG_TAG "hello_hidl" #include using android::sp; using jelly::hardware::hello_hidl::V1_0::IHello; using android::hardware::Return; using android::hardware::hidl_string; int main(){ android::sp hw_device = IHello::getService(); if (hw_device == nullptr) { ALOGD("failed to get hello-hidl"); return -1; } ALOGD("success to get hello-hidl...."); Return total = hw_device->addition_hidl(3,4); hw_device->write("hello"); hw_device->read([&](hidl_string result){ ALOGD("%s\n", result.c_str()); }); return 0; } Android.bp: cc_binary { name: "hello_hidl_test", srcs: ["hello_hidl_test.cpp"], vendor: true, shared_libs: [ "liblog", "jelly.hardware.hello_hidl@1.0", "libhidlbase", "libhidltransport", "libhwbinder", "libutils", ], } 2.6 selinux 配置 在 device/Jelly/Rice14/sepolicy 目录下添加: device.te 中添加如下内容: type hello_hidl_dev_t, dev_type; hwservice.te: type hello_hidl_hwservice, hwservice_manager_type; hello_hidl.te: type hello_hidl, domain; type hello_hidl_exec, exec_type, vendor_file_type, file_type; init_daemon_domain(hello_hidl); add_hwservice(hello_hidl, hello_hidl_hwservice) hwbinder_use(hello_hidl) allow hello_hidl hidl_base_hwservice:hwservice_manager { add }; allow hello_hidl hello_hidl_dev_t:chr_file { open read write }; binder_call(hello_hidl,hwservicemanager) get_prop(hello_hidl,hwservicemanager_prop) hwservice_contexts: jelly.hardware.hello_hidl::IHello u:object_r:hello_hidl_hwservice:s0 hello_hidl_test.te type hello_hidl_test, domain; type hello_hidl_test_exec, exec_type, vendor_file_type, file_type; domain_auto_trans(shell, hello_hidl_test_exec, hello_hidl_test); get_prop(hello_hidl_test, hwservicemanager_prop) allow hello_hidl_test hello_hidl_hwservice:hwservice_manager find; hwbinder_use(hello_hidl_test); 在 file_contexts 中添加: /dev/hello u:object_r:hello_hidl_dev_t:s0 /vendor/bin/hw/jelly\.hardware\.hello_hidl@1\.0-service u:object_r:hello_hidl_exec:s0 注意,file_contexts 最后必须留一行空行,不然会编译失败 2.7 编译执行 接着在 device/Jelly/Rice14/Rice14.mk 中添加如下内容: BOARD_SEPOLICY_DIRS += \ device/Jelly/Rice14/sepolicy PRODUCT_PACKAGES += \ jelly.hardware.hello_hidl@1.0-service \ hello_hidl_test \ jelly.hardware.hello_hidl@1.0-impl \ 然后整编系统: source lunch 1 make -j16 最后测试: # 执行客户端程序 hello_hidl_test & # 查看 log # logcat | grep hello 05-03 14:20:19.816 1555 1555 D : hello-hidl is starting... 05-03 14:20:19.816 1555 1555 I ServiceManagement: Registered jelly.hardware.hello_hidl@1.0::IHello/default (start delay of 36ms) 05-03 14:20:19.816 1555 1555 I ServiceManagement: Removing namespace from process name jelly.hardware.hello_hidl@1.0-service to hello_hidl@1.0-service. 05-03 14:20:38.843 3469 3469 D hello_hidl: success to get hello-hidl.... 05-03 14:20:38.843 1555 1563 E hello_hidl: hello_hidl service is init success....a :3,b:4 05-03 14:20:38.844 1555 1563 D hello_hidl: service is writing 05-03 14:20:40.846 1555 1563 D hello_hidl: service is reading 05-03 14:20:40.847 3469 3469 D hello_hidl: hello 可以看到我们的客户端已经访问到服务端了。 后续 以上是通过 hwbinder 跨进程通信的方式,来实现的。对于效率较高的模块,我们还是希望通过 dlopen 直接加载的方式来使用 hal 层。Android 的 Hidl 框架也是提供了支持的。这种方式称为直通模式(passthrough),这部分内容会在下一篇教程中讲解。 参考资料 Android系统开发入门-11.添加hidl服务 Android系统开发入门-12.hidl服务回调 Android HIDL模型下HAL Service添加SELinux规则实战 ROC-RK3399-PC Pro Android10 源码 一个简单的HIDL开发笔记 简单HIDL HAL的实现 hidl 服务启动流程 SElinux权限配置 AndroidO Treble架构下Hal进程启动及HIDL服务注册过程 Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要 系统角度解读Android P新特性 Android : 供应商原生开发套件 (VNDK) android 自定义驱动(第三篇:HIDL服务端) --- 由 答源 DAN 底座 1.4.1 生成 · 2026-11-11T03:33:07+00:00