文章目录
- 前言
- HDF 大体的认识
- HDF 核心模块 和 关键结构体
- 总结
一、前言
作为鸿蒙驱动开发来说,HDF框架和分布式软总线是非常重要的内容,可以这么说,正是这两部分内容,让鸿蒙系统跟android 和 ios 有了很大的不同。
原本的驱动开发,需要为每个设备写不同驱动,换个硬件还要重写代码,很容易出问题。而鸿蒙有了HDF框架,就变的不一样了。
用一句化来简单的概括,就是对驱动开发,进行抽象。更高层次的抽象,使得驱动开发,变得更简单。一次开发,在鸿蒙各种设备上都能使用。
正是因为更高层的抽象,使驱动开发简单,HDF框架就需要进行系统层抽象,提供标准驱动模型,还有统一平台的操作接口。最终实现一次开发,多端部署。
接下来,我们就一步步分析HDF框架,再到源码中的关键流程。
二、HDF 大体的认识
1、怎么使用HDF框架
cpp
1、通过配置hcs文件,适配不同的硬件。
2、HDF框架解析 hcs 配置,获取驱动信息。
3、驱动实现的Bind 函数被调用,进行服务接口的实例化和绑定
4、Init函数执行硬件初始化,最终通过 HdfDeviceNodePublishPublicService 等接口,
按照配置policy 将服务发布到全局管理器,供其他组件发现和获取。
5、应用 可以通过 DevSvcManagerClntGetService 接口, 传入服务名称,
获取对应的驱动服务对象,进而操作硬件。
2、HDF 核心设计思想
通过 OSAL 和 Platform 抽象层,屏蔽不同内核和芯片平台的差异。(不同内核,比如: LiteOs 和 Linux 内核。 不同的芯片,比如:海思、高通)
也就是 HDF 充当了翻译的工作, 不同内核层面:比如 驱动需要分配内存内存空间 OsalMemAlloc,HDF框架会进行翻译,在Linux 上运行,就会翻译成 malloc。 在 LiteOs 上就会翻译成LOS_MemAlloc。
不同平台层面: 比如驱动说,我要点灯,HDF会根据hcs 配置文件,知道在芯片A上,点灯的这个动作对应的操作地址 0x1000。而在芯片B上,对应的地址是 0x1010。
hcs配置文件的作用:实现了硬件配置 和 驱动代码的解耦。
3、一个 LED 驱动实现流程
以一个简单的LED 驱动为例,这样能更清楚的看到整个大概流程。
cpp
1、配置hcs文件:要定义LED设备,指定驱动源码的路径、设置GPIO引脚,设置发布策略等
2、驱动的实现: 在驱动代码中实现 Bind、Init、Release 函数,还有LED开关的逻辑
3、应用调用驱动: 应用通过 DevSvcManagerClntGetService("sample_driver") 来获取驱动服务,
拿到代表驱动的 HdfDeviceObject 后,通过 service->Dispatch 方法,
来发送控制命令,来点亮或熄灭 LED。
cpp
================================下面是更深入一点认识================================
三、核心模块 和 关键结构体
1、HDF 框架的核心模块
cpp
1、核心管理层:DevmgrService, DevSvcManager
(主要目的: 驱动生命周期管理、服务注册与发现)
2、驱动宿主层:DevHostService,HdfDevice, HdfDeviceNode
(主要目的:驱动实例的承载,驱动加载 HdfDriverLoader)
3、平台抽象和模型:GPIO/I2C/SPI 等平台接口
(主要目的:提供标准驱动模型 和 统一平台操作接口)
4、配置解析:HCS Parser (主要目的:解析hcs文件,生成配置树供驱动读取)
大白话,简化下上面内容:DevmgrService 启动 Host进程,触发驱动加载流程,然后进行绑定,接着调用驱动Init函数,初始化之后通过 HdfDeviceNodePublishPublicService 进行服务的注册。
2、关键结构体 (HDF中涉及的主要结构体 DevmgrService 、DevSvcManager)
DevmgrService 设备管理服务
cpp
1、核心职责: 驱动生命周期管理
2、管理层次:管理Host 及 它下面的设备和驱动
3、主要工作内容:启动、停止Host,加载、卸载驱动,电源事件分发
4、管理对象:Host 、Device、DeviceNode
5、数据存储结构:Hosts 双向链表
6、暴露的接口:StartService, StartDeviceHost 等
DevSvcManager 服务管理器
cpp
1、核心职责:驱动服务注册与发现
2、管理层次:管理已发布的服务接口
3、主要工作内容: 维护服务注册表、提供服务的查询和获取
4、管理对象:由HdfDeviceObject 代表的驱动服务
5、数据存储结构:服务名到 HdfDeviceObject 的映射表(服务注册表)
6、暴露的接口:DevSvcManagerClntGetService
上面两个是极其容易混淆的,他们之间的关系概括如下:
1、驱动加载与服务注册:
cpp
1、当系统启动或动态加载驱动时,DevmgrService 负责启动对应的 Host 进程(也就是 DevHostService),并触发驱动的加载流程
2、驱动在初始化过程中,会通过 DeviceDriverBind 函数将自身的服务接口(具体的实现 IDeviceIoService)
绑定到对应的 HdfDeviceObject(驱动实例)的servce 字段上,这样只要拿到HdfDeviceObject,就能拿到驱动提供的方法了。
3、DeviceDriverBind 执行完之后,也就是驱动初始化之后,会通过HdfDeviceNodePublishService
最终调用 DevSvcManagerAddService 向DevSvcManager注册驱动的服务。
此时DevSvcManager 会将驱动服务的名称和对应的 HdfDeviceObject
存入DevSvcManager的服务的注册表 services 中(也就是 DListHead)。
OpenHarmony 4.0 源码中实际调用过程如下(从上往下调用,缩进部分是函数内部的操作)
cpp
从 device_manager.c 的入口 main 开始
main
DevmgrServiceGetInstance 实际是 DevmgrServiceCreate
DevmgrServiceConstruct
instance->StartService (StartService 实际是 DevmgrServiceStartService)
DevmgrServiceStartService
DevmgrServiceStartDeviceHosts 启动设备所有主机
DevmgrServiceStartDeviceHost 启动单个设备主机
DevmgrServiceStartHostProcess
DriverInstallerGetInstance 实际是 DriverInstallerCreate
installer->StartDeviceHost StartDeviceHost 实际是 DriverInstallerStartDeviceHost
DriverInstallerStartDeviceHost 创建并启动主机
DevHostServiceNewInstance 实际是 DevHostServiceCreate -> DevHostServiceConstruct
hostServiceIf->StartService 实际是 DevHostServiceStartService
DevHostServiceStartService
DevmgrServiceClntAttachDeviceHost(hostService->hostId, service) service 实际是 DevHostService
DevmgrServiceClntGetInstance 调用 DevmgrServiceCreate 调用 DevmgrServiceConstruct 获得 inst
devMgrSvcIf = inst->devMgrSvcIf;
devMgrSvcIf->AttachDeviceHost
AttachDeviceHost 实际是 DevmgrServiceAttachDeviceHost
DevHostServiceClntInstallDriver
devHostSvcIf->AddDevice (DevHostService 继承 IDevHostService 调用 AddDevice, AddDevice 实际是 DevHostServiceAddDevice)
DevHostServiceAddDevice
device->super.Attach(&device->super, devNode) (Attach 实际是 HdfDeviceAttach)
HdfDeviceAttach
nodeIf->LaunchNode(devNode) (LaunchNode 实际是 HdfDeviceLaunchNode)
HdfDeviceLaunchNode
HdfDeviceNodePublishPublicService 发布服务
DevSvcManagerClntAddService
DevSvcManagerAddService
到这里服务已经发布完成
2、服务发现与使用
当应用需要 调用驱动提供的服务时,它会通过 DevSvcManagerClntGetService,向DevSvcManager 查询指定名称的驱动服务。 DevSvcManager 在其服务注册表中,找到 HdfDeviceObject ,这样调用者就能使用驱动提供的服务接口。
大白话:DevmgrSevice 负责把驱动加载和初始化,HdfDeviceObject 主要提供具体的硬件服务,然后DevSvcManager负责给这个驱动宣传,告诉外界这个驱动能提供什么服务,以及如何联系(注册和发现)。
四、总体流程(关键结构体,再加上Hcs文件的解析,于是总的HDF框架如下)
cpp
1、HCS Parser 解析配置,生成配置树
2、DevmgrService 会根据配置树,读取驱动列表。通过下发加载指令,调用 DevHostService
3、DevHostService 会通过 HdfDriverLoader 解析、调用 Bind/Init 最终创建生成 HdfDevice(驱动实例)
4、HdfDevice 会初始化硬件/服务接口,通过 HdfDeviceNodePublishPublicService 发布服务,
最终把服务注册到 DevSvcManager 的注册表中
5、有了驱动的服务,这样应用就能通过 DevSvcManager 的注册表查询,获取驱动,并进行驱动的调用
HDF框架还是有很多内容的,当涉及用户态和内核态,还会牵扯到IPC,但一旦有了上面的蓝图,相信你看源码就有了方向, 也对HDF框架有了大体的认识。
总结
1、介绍HDF框架的好处
2、HDF 框架大体的认识
3、最后通过源码中的关键结构体,还有关键模块,理解HDF框架的整体运行流程
如果对你有一点点帮助,那是值得高兴的事情。:)
我的csdn:blog.csdn.net/shenshizhon...