OpenHarmony 进阶——HDF 驱动框架的原理小结

文章大纲

  • 引言
  • 一、HDF的驱动加载(驱动安装)方式
    • 1、动态加载(主要是uhdf)
    • 2、静态加载(主要是khdf)
      • 2.1、驱动入口实现
        • 2.1.1、Bind接口
        • 2.1.2、Init接口
        • 2.1.3、Release接口
      • 2.2、HDF_INIT 驱动入口符号
      • 2.3、获取驱动列表
      • 2.4、获取设备列表
      • 2.5、设备与驱动的匹配
      • 2.6、HDF驱动框架的启动
      • 2.7、驱动静态加载的全流程总结
  • 二、驱动框架交互流程
  • 三、驱动的开发
    • 3.1、实现驱动的业务功能逻辑的三个接口
    • 3.2、定义用于注册驱动入口对象的结构体DriverEntry
    • 3.3、驱动配置信息
      • 3.3.1、驱动设备描述(必选)
      • 3.3.2、驱动私有配置信息(可选)
      • 3.3.3、配置SeLinux

引言

前面HDF 驱动框架基础理论入门文章简单的总结了HDF最基础的理论知识,接下来好好学习下HDF的一些核心工作原理。

一、HDF的驱动加载(驱动安装)方式

1、动态加载(主要是uhdf)

传统so方式,先dlopen打开指定的so,驱动程序通过指定symbol方式找到驱动函数入口进行加载。

2、静态加载(主要是khdf)

OpenHarmony驱动主要部署在内核态,当前主要采用静态链接方式,随内核子系统编译和系统镜像打包。HDF驱动加载包括按需加载和按序加载。按需加载是HDF框架支持驱动在系统启动过程中默认加载,或者在系统启动之后动态加载;按序加载是HDF框架支持驱动在系统启动的过程中按照驱动的优先级进行加载。HDF框架定义的驱动按需加载方式的策略是由配置文件中的 preload 字段来控制,preload 字段的取值范围以及含义如下:
在这里插入图片描述
驱动的按序加载是通过配置文件中的 priority(取值范围为整数 0 到 200)来决定的,priority 值越小,表示的优先级越高。驱动的加载顺序,优先根据 host 的 priority 决定,如果host 的 priority 相同,再根据 host 内的驱动 priority 值来决定加载顺序。
在这里插入图片描述
采用将驱动程序通过Scatter编译到指定的Section,再通过访问指定Section的地址找到驱动函数入口进行加载,如下是内核态驱动静态加载具体流程:

2.1、驱动入口实现

在HDF驱动框架中,HdfDriverEntry对象用于描述驱动入口的实现:

struct HdfDriverEntry {int32_t moduleVersion;const char *moduleName;int32_t (*Bind) (struct HdfDeviceObject *device0bject) ;int32_t (*Init) (struct HdfDeviceObject *device0bject) ;void (*Release) (struct HdfDevice0bject *device0bject) ;
}
2.1.1、Bind接口

用于驱动接口实例化绑定,若需要发布驱动接口,会在驱动加载过程中被调用,实例化该接口的驱动服务和DeviceObject绑定。驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架。该接口的作用主要是完成驱动设备和设备服务接口的bind动作。

在HdfDriverEntry的Bind方法中,必须完成全部驱动服务接口的绑定,禁止将服务接口未定义或定义为空,因为驱动定义的服务接口,均是对外暴露的,如果未定义或定义为空,可能会导致外部调用时产生异常,从而降低驱动的可靠性。

2.1.2、Init接口

当框架完成设备绑定动作后,就开始调用驱动初始化接口,初始化成功后,驱动框架根据配置文件决定是对外创建设备服务接口,还是接口只对当前服务可见。如果Init初始化失败,驱动框架就会主动释放创建的设备接口等信息。

2.1.3、Release接口

用于驱动的卸载,在该接口中需要释放驱动实例的软硬件资源。当需要卸载驱动时,驱动框架先通过该接口通知驱动程序释放资源,然后再释放其他内部资源。

2.2、HDF_INIT 驱动入口符号

系统启动时会执行HDF_INIT宏,

int SampleDriverBind(struct HdfDevice0bject *device0bject)
int SampleDriverInit (struct HdfDeviceObject *device0bject)
void SampleDriverRelease(struct HdfDeviceObject *device0bject)struct HdfDriverEntry g_sampleDriverEntry = {.moduleVersion = 1,.moduleName = "sample_driver",.Bind = SampleDriverBind, // 职责:绑定驱动对外提供的服务接口到HDF.Init = SampleDriverInit, // 职责:初始化驱动自身的业务.Release = SampleDriverRelease, // 职责:释放驱动资源,发生异常时也会调用
};
HDF_INIT (g_sampleDriverEntry) ;

宏展开后,在宏里定义了一个驱动的模块名+HdfEntry的符号并放到.hdf.section,即在编译时通过Scatter文件把HDF_INIT为开头的模块module的指针全部放到.hdf.section,放的Driver Entry的指针。

#define HDF_SECTION__attribute__((section(.hdf.driver”)))
#define HDF_DRIVER_INIT(module) \
const size_t USED_ATTR module##HdfEntry HDF_SECTION = (size_t)(&(module))

在这里插入图片描述
可以看到 HDF_INIT 宏是定义了一个“驱动模块名+HdfEntry”的符号放到".hdf.driver"所在 section,该符号指向的内存地址即为驱动程序入口结构体的地址。这个特殊的 section 将用于开机启动时查找设备驱动。

2.3、获取驱动列表

HDF 驱动框架通过将驱动程序入口符号的地址集中存放到一个特殊的 section 来实现对驱动的索引,这个 section 的开头和末尾插入了_hdf_drivers_start、_hdf_drivers_end 两个特殊符号,用于标记这个 section 的范围,两个特殊符号之间的数据即为驱动实现指针。
在这里插入图片描述

2.4、获取设备列表

在这里插入图片描述
系统启动时会根据Device Info创建对应的Host 节点。配置文本编译后会变成二进制格式的配置文件,其中设备相关信息被存放在一个用“hdf_manager”标记的 device_info 配置块中,host的内容以块的形式在 device_info 块中依次排列,host 块中记录了 host 名称、启动优先级和设备列表信息。设备信息中的 moduleName 字段将用于和驱动程序入口中的 moduleName 进行匹配,从而为设备匹配到正确的驱动程序。从而为设备匹配到正确的驱动程序,完成设备与驱动的匹配,具体流程图如下:
在这里插入图片描述

2.5、设备与驱动的匹配

系统启动时Device Manager和Device Host 即HDF框架首先启动,在系统启动时,驱动框架先启动(如何先启动?),通过解析配置文件获取到设备列表,通过读取”.hdf
drivers"段读取到驱动程序(Driver Entry)列表,然后遍历设备列表与驱动程序列表进行匹配,并加
载匹配成功的驱动。驱动框架有两大核心管理者:

  • DeviceManager 负责设备的管理,包括设备加载、卸载和查询等设备相关功能。
  • DeviceServiceManager 负责管理设备发布的接口服务,提供接口服务的发布、查询等功能。
    驱动加载主要由 DeviceManager 主导,首先 DeviceManager 要解析配置文件中的Host 列表,根据Host 列表中的信息来实例化对应的Host 对象。Host 解析配置文件获取到关联的设备列表,遍历设备列表去获取与之匹配的驱动程序名称,然后基于驱动程序名称遍历前面提到的.hdf.driver section 获得驱动程序地址。

2.6、HDF驱动框架的启动

HDF驱动框架通过late_initcall 启动

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.7、驱动静态加载的全流程总结

  • 在系统启动时,DeviceManagerInit通过late_initcall先启动。
  • Device Manager 根据 Device Information 信息,解析配置文件中的Host 列表,根据 Host 歹
    表中的信息来实例化对应的 Host 对象。
  • Host遍历设备列表去获取与之匹配的驱动程序名称,然后基于驱动程序名称遍历.hdf.driver sec
    on 获得驱动程序地址
  • 设备与驱动匹配成功之后,获取指定驱动的入口地址,加载对应的设备驱动程序。
  • 调用指定驱动的 Bind 接口,用于关联设备和服务实例。
  • 调用指定驱动的 Init 接口,用于完成驱动的相关初始化工作。
  • 如果驱动被卸载或者因为硬件等原因Init 接口返回失败,Release 将被调用,用于释放驱动申请
    的各类资源。

二、驱动框架交互流程

OpenHarmony 系统 HDF 驱动框架主要由驱动基础框架、驱动程序、驱动配置文件和驱动接口这四个部分组成。

  • HDF 驱动基础框架提供统一的硬件资源管理,驱动加载管理以及设备节点管理等功能。驱动框架采用的是主从模式设计,由 Device Manager 和 Device Host 组成。Device Manager 提供了统一的驱动管理,Device Manager 启动时根据 Device Information 提供驱动设备信息加载相应的驱动 Device Host,并控制 Host 完成驱动的加载。Device Host 提供驱动运行的环境,同时预置 Host Framework 与 Device Manager 进行协同,完成驱动加载和调用。根据业务的需求 Device Host 可以有多个实例。
  • 驱动程序实现驱动具体的功能,每个驱动由一个或者多个驱动程序组成,每个驱动程序都对应着一个 Driver Entry。Driver Entry 主要完成驱动的初始化和驱动接口绑定功能。
  • 驱动配置文件.hcs 主要由设备信息(Device Information)和设备资源(Device Resource)组成。Device Information 完成设备信息的配置。如配置接口发布策略,驱动加载的方式等。Device Resource 完成设备资源的配置。如 GPIO 管脚、寄存器等资源信息的配置。
  • 驱动接口 HDI(Hardware Driver interface )提供标准化的接口定义和实现,驱动框架提供 IO Service和IO Dispatcher 机制,使得不同部署形态下驱动接口趋于形式一致。
    在这里插入图片描述

驱动框架完成大部分驱动加载的动作,用户只需注册自己所需的接口和配置,然后驱动框架就会解析配置的内容,完成驱动加载和初始化动作。

三、驱动的开发

基于HDF(Hardware Driver Foundation开发驱动,开发者只需注册所需接口和配置,驱动框架就会解析配置内容,完成驱动加载和初始化动作:

3.1、实现驱动的业务功能逻辑的三个接口

HDF_INIT(g_sampleDriverEntry); 系统内核自动调用的一个宏,驱动入口对象注册到HDF框架,会调用HDF_INIT函数将驱动入口地址注册到HDF框架。

当调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。所以首先就是驱动入口部分相关的Bind, Init 和Release三个接口:

  • Bind接口,驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
    该接口的作用主要是完成驱动设备和设备服务接口的bind动作。
int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject)
{return HDF_SUCCESS;
}
  • Init接口
    在HdfDriverEntry的Init方法中,应当调用HdfDeviceSetClass接口,对驱动的类型进行定义。驱动的类型可以用于归类当前设备的驱动程序,也可以用来查询当前设备的驱动能力。为了便于后续驱动的统一管理,建议通过HdfDeviceSetClass接口来设置当前驱动的类型。
int32_t SampleDriverInit(struct HdfDeviceObject *deviceObject)
{// 设置驱动的类型为DISPLAYif (!HdfDeviceSetClass(deviceObject, DEVICE_CLASS_DISPLAY)) {HDF_LOGE("HdfDeviceSetClass failed");return HDF_FAILURE;}return HDF_SUCCESS;
}
  • Release接口
    当用户需要卸载驱动时,驱动框架先通过该接口通知驱动程序释放资源,然后再释放其他内部资源
void SampleDriverRelease(struct HdfDeviceObject *deviceObject)
{//驱动资源释放的接口 Release all resources.return;
}

3.2、定义用于注册驱动入口对象的结构体DriverEntry

定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,主要是把三个接口赋值,相当于是完成映射。

struct HdfDriverEntry g_deviceSample = {.moduleVersion = 1,.moduleName = "sample_driver", .Bind = SampleDriverBind,.Init = SampleDriverInit,.Release = SampleDriverRelease,
};

3.3、驱动配置信息

驱动配置包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息。HDF使用HCS作为配置描述源码,内容以 Key-Value 键值对为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。HC-GEN (全称 HDF Configuration Generator) 是 HCS 配置转换工具,可以将 HDF 配置文件转换为软件可读取的文件格式:在弱性能环境中,转换为配置树源码,驱动可直接调用 C代码获取配置;在高性能环境中,转换为 HCB(HDF Configuration Binary)二进制文件,驱动可使用 HDF框架提供的配置解析接口获取配置。HCS经过HC-GEN编译生成HCB文件,HDF驱动框架中的HCS Parser模块会从HCB文件中重建配置树,HDF驱动模块使用HCS Parser提供的配置读取接口获取配置内容。

3.3.1、驱动设备描述(必选)

HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述,因此基于HDF框架开发的驱动必须要在HDF框架定义的device_info.hcs配置文件中添加对应的设备描述,驱动的设备描述填写如下所示:

sample_host :: host{hostName = "host0"; //host名称,host节点是用来存放某一类驱动的容器。priority = 100; //host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序。device_sample :: device { //sample设备节点。device0 :: deviceNode { //sample驱动的DeviceNode节点。policy = 1; //驱动服务发布的策略priority = 100; //驱动启动优先级(0-200),值越大优先级越低,建议默认配 100,优先级相同则不保证 device 的加载顺序preload = 0; //驱动按需加载字段permission = 0664;//驱动创建设备节点权限moduleName = "sample_driver"; //驱动名称,该字段的值必须和驱动入口结构的moduleName值一致serviceName = "sample_service"; //驱动对外发布服务的名称,必须唯一deviceMatchAttr = "sample_config";//驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等。}}
}

3.3.2、驱动私有配置信息(可选)

如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息,HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject 中的property里面,通过Bind和Init传递给驱动,驱动的配置信息示例如下:

root {  SampleDriverConfig {sample_version = 1; sample_bus = "I2C_0"; match_attr = "sample_config";   //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致}
}

3.3.3、配置SeLinux

在4.0后必须配置SeLinux 否则即使加载了So也无法正常被拉起,具体配置规则根据业务自行配置。

未完待续…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/72400.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大模型应用:多轮对话(prompt工程)

概述 在与大型语言模型(如ChatGPT)交互的过程中,我们常常体验到与智能助手进行连贯多轮对话的便利性。那么,当我们开启一个新的聊天时,系统是如何管理聊天上下文的呢? 一、初始上下文的建立 1. 创建新会…

如何为JAR设置定时重启?

AI越来越火了,我们想要不被淘汰就得主动拥抱。推荐一个人工智能学习网站,通俗易懂,风趣幽默,最重要的屌图甚多,忍不住分享一下给大家。点击跳转到网站 前面我们说过了如何将jar交由Systemctl管理,下面我们…

神码AC-AP无线部署

神码AC-AP无线部署: 1.设置基础网络 交换机设置 service dhcp ! ip dhcp pool ap (AP用地址) network-address 10.1.1.0 255.255.255.0 default-router 10.1.1.254 option 43 hex 010401010101 (AC IP地址16进制&#…

【Redis】常用命令汇总

Redis 作为高性能的键值存储数据库,提供了丰富的命令集,主要涵盖 字符串 (String)、哈希 (Hash)、列表 (List)、集合 (Set)、有序集合 (ZSet)、键 (Keys)、Geo(地理位置)、HyperLogLog(基数统计)、Bitmap&a…

Redis - 高可用实现方案解析:主从复制与哨兵监控

文章目录 Pre概述Redis 高可用实现方案一、主从复制机制1.1 全量同步流程1.2 增量同步(PSYNC)流程 二、哨兵监控机制2.1 故障转移时序流程 三、方案对比与选型建议四、生产环境实践建议 Pre Redis-入门到精通 Redis进阶系列 Redis进阶 - Redis主从工作…

2025年渗透测试面试题总结-02(题目+回答)

网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 阿里云安全实习 一、代码审计经验与思路 二、越权漏洞原理与审计要点 三、SSRF漏洞解析与防御 四、教…

水滴tabbar canvas实现思路

废话不多说之间看效果图,只要解决了这个效果水滴tabbar就能做出来了 源码地址 一、核心实现步骤分解 布局结构搭建 使用 作为绘制容器 设置 width=600, height=200 基础尺寸 通过 JS 动态计算实际尺寸(适配高清屏) function initCanvas() {// 获取设备像素比(解决 Re…

解决各大浏览器中http地址无权限调用麦克风摄像头问题(包括谷歌,Edge,360,火狐)后续会陆续补充

项目场景: 在各大浏览器中http地址调用电脑麦克风摄像头会没有权限,http协议无法使用多媒体设备 原因分析: 为了用户的隐私安全,http协议无法使用多媒体设备。因为像摄像头和麦克风属于可能涉及重大隐私问题的API,ge…

网络安全蜜罐产品研究现状

🍅 点击文末小卡片 ,免费获取网络安全全套资料,资料在手,涨薪更快 一、知识点总结 1、蜜罐(Honeypot):诱捕攻击者的一个陷阱。 2、蜜网(Honeynet):采用了技术…

el-card 结合 el-descriptions 作为信息展示

记录下el-card 组合 el-descriptions 实现动态展示信息 文章结构 实现效果1. el-descriptions 组件使用1.1 结合v-for实现列表渲染1.2 解析 2. 自定义 el-descriptions 样式2.1 修改背景色、字体颜色2.2 调整字体大小2.3 解析 3. el-card 结合 el-descriptions 作为信息展示3.…

【Java---数据结构】链表 LinkedList

1. 链表的概念 链表用于存储一系列元素,由一系列节点组成,每个节点包含两部分:数据域和指针域。 数据域:用于存储数据元素 指针域:用于指向下一个节点的地址,通过指针将各个节点连接在一起,形…

python-leetcode-不同的二叉搜索树 II

95. 不同的二叉搜索树 II - 力扣(LeetCode) # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class S…

动态规划/贪心算法

一、动态规划 动态规划 是一种用于解决优化问题的算法设计技术,尤其适用于具有重叠子问题和最优子结构性质的问题。它通过将复杂问题分解为更简单的子问题,并保存这些子问题的解以避免重复计算,从而提高效率。 动态规划的核心思想 最优子结…

2月28日,三极管测量,水利-51单片机

众所周知,三极管(BJT)有三个管脚,基极(B)、集电极(C)、发射极(E),在实际应用中,不可避免地会遇到引脚辨别的问题。接下来就讲下三极管…

Linux常见基本指令(二)

目录 1、Linux基础指令 文本查看 cat指令 more指令 less指令 head指令&tail指令 时间相关指令 查找、搜索相关指令 find指令 which指令 whereis指令 alias指令 grep指令 打包压缩和解压缩 zip指令(压缩) unzip(解压&…

Day 55 卡玛笔记

这是基于代码随想录的每日打卡 所有可达路径 题目描述 ​ 给定一个有 n 个节点的有向无环图,节点编号从 1 到 n。请编写一个函数,找出并返回所有从节点 1 到节点 n 的路径。每条路径应以节点编号的列表形式表示。 输入描述 ​ 第一行包含两个整数…

2. 在后端代码中加入日志记录模块

1. 说明 日志模块基本上是每一个软件系统开发中必不可少的,主要用于持久记录一些代码运行中的输出信息,辅助编码人员进行代码调试,以及后期软件上线运行报错分析。在Python中加入日志模块比较简单,只需要借助logging和RotatingFi…

【Vue3】浅谈setup语法糖

Vue3 的 setup 语法糖是通过 <script setup> 标签启用的特性&#xff0c;它是对 Composition API 的进一步封装&#xff0c;旨在简化组件的声明式写法&#xff0c;同时保留 Composition API 的逻辑组织能力。以下是其核心概念和原理分析&#xff1a; 一、<script setu…

物联网小范围高精度GPS使用

在园区内实现小范围高精度GPS&#xff08;全球定位系统&#xff09;定位&#xff0c;通常需要结合多种技术来弥补传统GPS在精度和覆盖范围上的不足。以下是实现小范围高精度GPS定位的解决方案&#xff0c;包括技术选择、系统设计和应用场景。 一、技术选择 在园区内实现高精度…

【前端】前端设计中的响应式设计详解

文章目录 前言一、响应式设计的定义与作用二、响应式设计的原则三、响应式设计的实现四、响应式设计的最佳实践总结 前言 在当今数字化时代&#xff0c;网站和应用程序需要适应各种设备&#xff0c;从桌面电脑到平板电脑和手机。响应式设计应运而生&#xff0c;成为一种可以适…