当前位置: 首页 > news >正文

openharmony5.0中hdf框架中实现驱动程序的动态加载和管理的技术细节分析

在分析openharmony的hdf框架的设备驱动加载器(IDriverLoader)时发现在创建实例时会首先判断一下是否完成了驱动入口的构建(HdfDriverEntryConstruct),如果没有构建会重新构建,这与我开始以为的不一致(我一直以为是采用的linux内核方式,只是由hdf来将驱动注册进去),oh将这部分由hdf框架接管了。下面我便将这部分实现驱动程序的动态加载和管理的技术细节进行分析。

实现驱动程序的动态加载和管理主要过程如下:

  • 驱动注册通过 HDF_DRIVER_INIT 宏将驱动入口点注册.hdf.driver 段。
  • 驱动加载:由设备管理器解析配置文件并调用驱动的 BindInit 函数
  • 服务发布与订阅:通过 HdfDeviceNodePublishServiceHdfDeviceSubscribeService 实现。
  • 驱动卸载:通过 Release 函数释放驱动资源

在使用前首先看下驱动是如何应用的(有个大概的使用思路)

struct HdfDriverEntry g_i2cManagerEntry = {//驱动程序的入口信息.moduleVersion = 1,//驱动模块的版本号.Bind = I2cManagerBind,//用于将驱动与设备对象关联.Init = I2cManagerInit,//用于完成驱动的初始化操作.Release = I2cManagerRelease,//用于释放驱动占用的资源.moduleName = "HDF_PLATFORM_I2C_MANAGER", //必须与 device_info.hcs 文件中的设备名称一致
};
HDF_INIT(g_i2cManagerEntry);//用于将驱动程序的入口结构注册到 HDF 框架中

下面分别介绍动态加载和管理的四个主要过程。

驱动注册

在 OpenHarmony 中,驱动程序通过 HDF_INIT宏注册到 .hdf.driver 段中。这个宏将驱动程序的入口点信息存储到特定的内存段,供运行时加载

在hdf_core\interfaces\inner_api\host\shared\hdf_device_desc.h文件中我们可以看到实际HDF_INIT宏实际调用的为HDF_DRIVER_INIT。

#define HDF_INIT(module)  HDF_DRIVER_INIT(module)
  • HDF_DRIVER_INIT说明

结合hdf_core\framework\core\common\include\host\hdf_device_section.h代码的宏定义

//这用于确保驱动程序的入口点不会被编译器优化掉
#define USED_ATTR __attribute__((used))//用于将变量或函数放置到指定的内存段(section)中
//将驱动程序的入口点信息存储到 .hdf.driver 段中
//这个段在运行时会被 HDF 框架扫描,以获取驱动程序的入口点
#define HDF_SECTION __attribute__((section(".hdf.driver")))

可得注册一个驱动程序模块

#define HDF_DRIVER_INIT(module) \const size_t USED_ATTR module##HdfEntry HDF_SECTION = (size_t)(&(module))

定义一个全局变量(module##HdfEntry),其名称由驱动模块名(module)拼接而成。使用 USED_ATTR 确保该变量不会被优化掉。使用 HDF_SECTION 将该变量放置到 .hdf.driver 段中。将该变量的值设置为驱动模块的地址(&(module))。这样,驱动程序的入口点信息就会被存储到 .hdf.driver 段中,供运行时扫描和加载。

此时便有个疑问,我们只知道驱动程序的入口点信息就会被存储到 .hdf.driver 段中,那我们如何访问或使用它呢?

答案就在hdf_core\framework\core\common\include\host\hdf_device_section.h文件中的以下两个函数中

/*** 这个宏定义用于获取指定变量的地址。* 声明一个外部变量(extern type name)。* 返回该变量的地址(&(name))。* 这个宏主要用于定义 _hdf_drivers_start 和 _hdf_drivers_end,标记驱动程序入口点的范围。*/
#define HDF_DRIVER_SEC_NAME(type, name) \({extern type name;            \&(name);                       \})/**通过 HDF_DRIVER_SEC_NAME 宏,获取 _hdf_drivers_start 和 _hdf_drivers_end 的地址。
这些地址在运行时被 HDF 框架用来遍历 .hdf.driver 段中的驱动程序入口点。
HDF 框架可以通过 _hdf_drivers_start 和 _hdf_drivers_end 之间的范围,动态加载所有注册的驱动程序。
*/
#define HDF_DRIVER_BEGIN() HDF_DRIVER_SEC_NAME(size_t, _hdf_drivers_start)
#define HDF_DRIVER_END() HDF_DRIVER_SEC_NAME(size_t, _hdf_drivers_end)

经过对上述代码的理解我们知道如何来使用了,但是此时还有一个疑问没有解决,**那就是_hdf_drivers_start和_hdf_drivers_end变量在哪定义的呢?它在哪被赋值了呢?**通过对代码的搜索可以看到在链接器脚本(vmlinux.lds.S)中有相关定义。如下图所示:

在这里插入图片描述

这段代码的作用是:

  1. 定义一个名为 .init.hdf_table 的段
  2. 该段的起始位置设置 _hdf_drivers_start 符号
  3. 所有标记为 .hdf.driver 的数据(即驱动程序入口点)放入该段。
  4. 在该段的结束位置设置 _hdf_drivers_end 符号

这部分的内容有点绕为了更好的总结在这儿弄了个使用demo,可以供参考理解:

// 定义一个驱动程序模块
struct HdfDriverEntry MyDriver;// 注册驱动程序模块
HDF_DRIVER_INIT(MyDriver);// 在代码中使用 HDF_DRIVER_BEGIN 和 HDF_DRIVER_END 获取驱动程序范围
size_t *start = HDF_DRIVER_BEGIN();
size_t *end = HDF_DRIVER_END();// 遍历驱动程序入口点
for (size_t *ptr = start; ptr < end; ptr++) {struct HdfDriverEntry *driver = (struct HdfDriverEntry *)(*ptr);// 加载驱动程序
}

驱动加载

驱动加载过程由 HDF 框架的设备管理器(Device Manager)和设备主机(Device Host)协同完成,这部分详细的过程比较复杂(本周应该能写完这篇汇总的),此处只列出核心点

  • 设备管理器初始化

    static int __init DeviceManagerInit(void)
    {int ret = DeviceManagerStart();if (ret < 0) {HDF_LOGE("%s start failed %d", __func__, ret);} else {HDF_LOGD("%s start success", __func__);}return ret;
    }
    late_initcall(DeviceManagerInit);
    

    设备管理器在系统启动时通过 late_initcall 初始化,并开始加载驱动。

  • 驱动加载流程

    1. 设备管理器解析配置文件(device_info.hcs),获取设备和驱动的映射关系
    2. 根据配置文件中的 preload 字段,决定驱动是按需加载还是按序加载
    3. 遍历 .hdf.driver 段,获取驱动程序的入口地址
    4. 调用驱动的 BindInit 函数完成初始化

代码调用过程如下:
在这里插入图片描述

int HdfDeviceLaunchNode(struct HdfDeviceNode *devNode)
{const struct HdfDriverEntry *driverEntry = devNode->driver->entry;int ret = DeviceDriverBind(devNode);if (ret != HDF_SUCCESS) {return ret;}ret = driverEntry->Init(&devNode->deviceObject);if (ret != HDF_SUCCESS) {return HDF_DEV_ERR_DEV_INIT_FAIL;}ret = HdfDeviceNodePublishService(devNode);if (ret != HDF_SUCCESS) {return HDF_DEV_ERR_PUBLISH_FAIL;}return ret;
}

服务发布与订阅

HDF 框架支持服务发布和订阅机制,允许驱动程序在初始化后发布服务接口,供其他模块使用

  • 服务发布

    int HdfDeviceNodePublishService(struct HdfDeviceNode *devNode);
    
  • 服务订阅

其他模块可以通过 HdfDeviceSubscribeService 订阅驱动服务。当驱动加载完成后,框架会将服务接口发布给订阅者

int HdfDeviceSubscribeService(struct HdfDeviceObject *deviceObject, const char *serviceName, struct SubscriberCallback callback);

对此部分内容需要详细了解的可以参考

驱动卸载

和驱动加载相对应的卸载驱动的函数为HdfDeviceUnlaunchNode,当驱动被卸载或初始化失败时,HDF 框架会调用驱动的 Release 函数释放资源:

static void HdfDeviceUnlaunchNode(struct HdfDeviceNode *devNode)
{....if (devNode->driver != NULL) {driverEntry = devNode->driver->entry;//从设备节点中获取驱动程序对象(devNode->driver)}if (driverEntry != NULL && driverEntry->Release != NULL) {driverEntry->Release(&devNode->deviceObject);//如果驱动程序的入口结构和 Release 函数指针都有效,调用 Release 函数释放驱动占用的资源,即驱动中传入的release函数}....
}

相关文章:

openharmony5.0中hdf框架中实现驱动程序的动态加载和管理的技术细节分析

在分析openharmony的hdf框架的设备驱动加载器(IDriverLoader)时发现在创建实例时会首先判断一下是否完成了驱动入口的构建(HdfDriverEntryConstruct)&#xff0c;如果没有构建会重新构建&#xff0c;这与我开始以为的不一致(我一直以为是采用的linux内核方式&#xff0c;只是由…...

TVS管学习记录

文章目录 前言一、TVS是什么&#xff1f;二、TVS关键参数1.反向截至电压**实际意义** 2.钳位电压**定义与作用****选择依据** **4. 实际应用示例****场景&#xff1a;通信端口的ESD保护** 3.反向截至电压和钳位电压的关联和区别**. 小结** 三、实际应用电路举例总结 前言 TVS管…...

数据库表的各种设计

本篇文章&#xff0c;主要讲解项目开发时&#xff0c;遇到不同的情况&#xff0c;要学会对数据库的表进行合理设计。 1、将表的某个字段&#xff0c;存到一张新表中 ①情况描述 ②操作步骤 第一步&#xff1a;创建role表 第二步&#xff1a;在user表中&#xff0c;删除role字…...

JWT使用教程

目录 JWT (JSON Web Token)1. JWT简介(1) 什么是JWT(2) JWT有什么用(3) JWT认证方式 2. JWT的组成部分3. 签名的目的4. JWT与Token的区别5 JWT的优势6 JJWT签发与验证token(1) 引入依赖(2) 创建 Token(3) 解析Token(4) 设置过期时间(5) 自定义claims 7. JWT自定义工具类 JWT (J…...

【大模型系列篇】如何解决DeepSeek-R1结构化输出问题,使用PydanticAl和DeepSeek构建结构化Agent

今日号外&#xff1a;&#x1f525;&#x1f525;&#x1f525; DeepSeek开源周&#xff1a;炸场&#xff01;DeepSeek开源FlashMLA&#xff0c;提升GPU效率 下面我们开始今天的主题&#xff0c;deepseek官方明确表示deepseek-r1目前不支持json输出/function call&#xff0c;可…...

老旧android项目编译指南(持续更)

原因 编译了很多项目&#xff0c;找到了一些可观的解决办法 1. android studio里面的jdk版本切换 jdk版本切换在这里&#xff0c;一般安卓开发需要用到4个版本的jdk,jdk8, jdk11, jdk17, jdk21新版的android stuio是默认使用高版本的jdk,所以切换版本是很有必要的 2. 命令…...

linux中安装部署Jenkins,成功构建springboot项目详细教程

参考别人配置Jenkins的git地址为https&#xff0c;无法连上github拉取项目&#xff0c;所以本章节介绍通过配置SSH地址来连github拉取项目 目录&#xff1a; 1、springboot项目 1.1 创建名为springcloudproject的springboot项目工程 1.2 已将工程上传到github中&#xff0c;g…...

AI开发利器:Anaconda

在Python开发过程中&#xff0c;不同的项目可能会依赖不同版本的Python以及各种不同版本的库。比如&#xff0c;项目A可能依赖Python 3.8和某个特定版本的numpy、TensorFlow和PyTorch&#xff0c;而项目B可能需要Python 3.9以及另一个版本的numpy库。如果直接在系统中安装Pytho…...

java网络编程--基于TCP协议的网络编程

Scoket介绍 利用 TCP 协议进行通信的两个应用程序是有主次之分的&#xff0c; 一个是服务器程序&#xff0c;一个是客户端程序&#xff0c; 两者的功能和编写方法不太一样&#xff0c; 其中 ServerSocket 类表示 Socket 服务器端&#xff0c;Socket 类表示 Socket 客户端。 服…...

PageHelper新发现

PageHelper 背景解决reasonablepageSizeZero 背景 今天发现了一个很有趣的现象&#xff0c;接手一个很老的项目springmvc项目、使用PageHelper分页实现常见的后端接口分页功能。但是发现当页码参数大于实际的页码数时、正常不应该返回数据&#xff0c;但是目前确一直返回数据不…...

Redis 数据结构和使用详解(带示例)

Redis 支持的主要数据结构及其对应操作命令的详细解释&#xff0c;结合具体使用场景和示例&#xff1a; 1. 字符串&#xff08;String&#xff09; 用途&#xff1a;存储文本、数值或二进制数据&#xff0c;适用于缓存、计数器等。 常用命令&#xff1a; SET key value&#x…...

深度解读 Chinese CLIP 论文:开启中文视觉对比语言预训练

目录 论文概述1.论文摘要2.论文脑图3.论文创新3.1模型构建3.2训练方法3.3数据构建3.4部署优化 4.模型架构 论文解析1. 引言2. 方法2.1数据说明2.2预训练方法2.2.1模型初始化方法2.2.2两阶段预训练方法 2.3预训练细节2.3.1模型初始化2.3.2第一阶段预训练2.3.3第二阶段预训练2.3.…...

SpringBoot 2 后端通用开发模板搭建(异常处理,请求响应)

目录 一、环境准备 二、新建项目 三、整合依赖 1、MyBatis Plus 数据库操作 2、Hutool 工具库 3、Knife4j 接口文档 4、其他依赖 四、通用基础代码 1、自定义异常 2、响应包装类 3、全局异常处理器 4、请求包装类 5、全局跨域配置 补充&#xff1a;设置新建类/接…...

【Oracle专栏】sqlplus显示设置+脚本常用显示命令

Oracle相关文档&#xff0c;希望互相学习&#xff0c;共同进步 风123456789&#xff5e;-CSDN博客 1.内容概述 本文主要针对oracle 运维中常用知识点进行整理&#xff0c;包括&#xff1a; 1&#xff09;sqlplus模式下&#xff0c;为了方便查询设置相应的行宽、列宽、行数。…...

DeepSeek 助力 Vue3 开发:打造丝滑的页眉(Header)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…...

JVM线程分析详解

java线程状态&#xff1a; 初始(NEW)&#xff1a;新创建了一个线程对象&#xff0c;但还没有调用start()方法。运行(RUNNABLE)&#xff1a;Java线程中将就绪&#xff08;ready&#xff09;和运行中&#xff08;running&#xff09;两种状态笼统的称为“运行”。 线程对象创建…...

【备赛】点亮LED

LED部分的原理图 led前面有锁存器&#xff0c;这是为了防止led会受到lcd的干扰&#xff08;lcd也需要用到这些引脚&#xff09;。 每次想要对led操作&#xff0c;就需要先打开锁存器&#xff0c;再执行操作&#xff0c;最后关闭锁存器。 这里需要注意的是&#xff0c;引脚配置…...

【音视频】编解码相关概念总结

NALU RTP PS流 三者总体关系 NALU在RTP中的应用&#xff1a;视频流的RTP传输通常将NALU作为基本的单元进行传输。每个RTP包携带一个或多个NALU&#xff0c;这些NALU包含了视频编码数据。RTP协议通过其头部信息&#xff08;如时间戳、序列号等&#xff09;帮助接收端重新排列和…...

Python爬虫(四)- Selenium 安装与使用教程

文章目录 前言一、简介及安装1. Selenium 简介2. Selenium 安装 二、Selenium 基本使用1. 导入Selenium2. 启动浏览器3. 打开网页4. 获取页面标题5. 关闭浏览器6. 完整示例代码 三、Selenium WebDriver1. 简介2. 基本操作2.1 启动浏览器2.2 关闭浏览器2.3 打开网页2.4 关闭当前…...

Node.js项目启动流程以及各个模块执行顺序详解

Node.js项目启动流程以及各个模块执行顺序的问题。首先&#xff0c;我需要仔细阅读并理解我搜索到的资料&#xff0c;从中提取关键信息&#xff0c;然后综合这些信息组织成一个结构化的回答。 首先&#xff0c;根据我搜索到的资料都详细描述了Node.js的启动流程&#xff0c;涉及…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...