当前位置: 首页 > 科技观察

HDC2021技术分论坛:OpenHarmony驱动框架解读和开发实践

时间:2023-03-19 11:25:31 科技观察

HDC2021技术分论坛:OpenHarmony驱动框架解读与开发实践差异较大,形态各异,大小不一,交互方式各不相同。解决设备适配问题无疑是实现万物互联的关键。但是,在驱动框架的开发和部署过程中,由于终端设备对硬件计算和存储能力的要求不同,设备厂商提供的设备软硬件操作接口不同,内核提供的操作接口也不同,这使得OEM厂商在部署System时需要花费大量精力来适配和维护驱动程序代码。能否提供一个跨芯片平台和跨内核的驱动框架,让设备驱动软件可以运行在不同的设备上?框架来满足这个要求。下面我们就带大家解读一下OpenHarmony驱动框架。一、OpenHarmony驱动框架解读1、设计目标为解决开发部署过程中遇到的困难,OpenHarmony驱动框架的设计目标如下:支持100K级容量设备的部署到G级,如手机、手环等。硬件IO抽象,屏蔽SoC芯片差异,兼容不同内核,如Linux、LiteOS等。屏蔽驱动与系统组件的交互。可动态拆卸,满足不同容量设备的部署。为不同容量的设备提供统一的配置界面。2、设计思路OpenHarmony驱动框架(以下简称HDF)为驱动与芯片平台和内核解耦提供了基础,规范了硬件驱动接口,实现了驱动软件在不同设备中的部署。HDF驱动框架架构如下图所示。图1驱动架构为实现设计目标,OpenHarmony驱动框架采用了以下核心设计思想:(1)弹性架构框架可动态伸缩:通过对象管理器实现不同容量设备的多态加载实现弹性伸缩部署。驱动可动态伸缩:支持设备驱动插件统一管理,实现设备驱动任意分层,积木式组合拼接(2)组件化设备模型,提供设备功能模型抽象,屏蔽设备驱动与系统交互的实现,并为开发者提供统一驱动开发接口,提供主流IC公版驱动能力,支持配置部署(3)标准化平台库,提供标准化内核和SoC硬件IO适配接口,兼容不同内核和SoC芯片,开发对外标准化平台驱动接口(4)统一配置接口为不同容量的设备构建新的配置语言,提供统一的配置接口,支持硬件资源配置和设备信息配置IC驱动形成公版驱动和通用设备功能nction模型支持部署不同的硬件芯片和不同的内核(LiteOS-M/LiteOS-A)。图2轻量级设备部署模式,面向标准设备。除了支持内核态驱动外,它还支持用户态驱动。用户态驱动的重点是构建抽象的设备模型,为系统提供统一的设备接口,兼容Linux原生驱动和HDF驱动。内核模式使用Linux驱动和HDF驱动共存的策略来提供端到端的解决方案。图3标准设备部署模式4.现状与演进目前HDF驱动框架支持Liteos-m、Liteos-a、Linux内核和OpenHarmony轻量级、标准级部署,在标准系统上同时支持内核态和用户态状态部署。图4OpenHarmony驱动框架演化图通过开发者的不断努力,OpenHarmony驱动框架正在不断完善和增强。在OpenHarmonyLTS3.0中,基础框架增加了热插拔设备的管理和HDI编译工具hdi-gen。驱动模型部分增加了对Audio、Camera、Senso、USBDDK等多个模块的支持。2、OpenHarmony驱动开发为了避免对特定内核的依赖,达到移植的目的,OpenHarmony驱动在开发时需要遵循以下约定:系统相关接口使用HDFOSAL接口;总线和硬件资源相关接口使用平台驱动提供的相关接口。基于HDF框架,驱动开发的通常过程包括驱动代码的实现、脚本的编译、配置文件的添加,以及用户态程序与驱动交互的过程。下面将详细介绍HDF驱动开发的一般步骤。1.实现驱动代码在HDF驱动框架中,HdfDriverEntry对象用于描述一个驱动实现。structHdfDriverEntry{int32_tmoduleVersion;constchar*moduleName;int32_t(*Bind)(structHdfDeviceObject*deviceObject);int32_t(*Init)(structHdfDeviceObject*deviceObject);void(*Release)(structHdfDeviceObject*deviceObject);};写一个简单的驱动,首先DriverEntry入口中主要有3个接口需要实现:Bind接口:实现驱动接口实例化绑定,如果需要释放驱动接口,会在驱动加载过程中调用,实例化接口的驱动服务并与DeviceObject绑定。当用户态发起调用时,会回调Bind中绑定的服务对象的Dispatch方法,在该方法中处理用户态调用的消息。Init接口:实现驱动或硬件的初始化,返回错误将停止驱动加载过程。Release接口:实现驱动的卸载,在该接口释放驱动实例的软硬件资源。基于HDF框架编写的一个简单的驱动代码如下。它的作用是环回用户态消息,即驱动程序收到用户态发送的消息后向用户态发送相同内容的消息:#include"hdf_base.h"#include"hdf_device_desc.h"#包括“hdf_log.h”#defineHDF_LOG_TAG“sample_driver”#defineSAMPLE_WRITE_READ0xFF00staticintEchoString(structHdfDeviceObject*deviceObject,structHdfSBuf*data,structHdfSBuf*reply){constchar*readData=HdfSbufReadString(data);if(readData==NULL){HDF_LOGE("%s:failedtoreaddata",__func__);returnHDF_ERR_INVALID_PARAM;}if(!HdfSbufWriteInt32(reply,INT32_MAX)){HDF_LOGE("%s:failedtoreaddata",__func__);returnHDF_FAILURE;}returnHdfDeviceSend/EObjectvent,id(device)/发送事件到用户模式}int32_tHdfSampleDriverDispatch(structHdfDeviceObject*deviceObject,intid,structHdfSBuf*data,structHdfSBuf*reply){constchar*readData=NULL;intret=HDF_SUCCESS;switch(id){switchSAMPLE_WRITE_READ:ret=EchoString(deviceObject,data,reply));中断;默认:HDF_LOGE("%s:unsupportedcommand");ret=HDF_ERR_INVALID_PARAM;}返回ret;}voidHdfSampleDriverRelease(structHdfDeviceObject*deviceObject){//这里释放驱动请求的软硬件资源};deviceObject->service=&testService;returnHDF_SUCCESS;}intHdfSampleDriverInit(structHdfDeviceObject*deviceObject){if(deviceObject==NULL){HDF_LOGE("%s::ptrisnull!",__func__);returnHDF_FAILURE;}HDF_LOGE("SampledriverInitsuccess");returnHDF_SUCCESS;}structHdfDriverEntryg_sampleDriverEntry={.moduleVersion=1,.moduleName="sample_driver",.Bind=HdfSampleDriverBind,.Init=HdfSampleDriverInit,.Release=HdfSampleDriverRelease,};HDF_INIT(g_sampleDriverEntry);在文件中添加驱动的配置信息(如vendor/hisilicon/xxx/config/device_info.hcs),配置目录与具体开发板关联,如下:root{device_info{match_attr="hdf_manager";templatehost{hostName="";priority=100;template设备{templateddeviceNode{policy=0;priority=100;preload=0;permission=0664;moduleName="";serviceName="";deviceMatchAttr="";}}}sample_host::host{hostName="host0";//主机名,主机节点用于存放某类驱动容器priority=100;//主机启动优先级(0-200),值越大优先级越低,建议配置100默认,同优先级不保证主机device_sample::device{//sampledevicenodedevice0::deviceNode{//sample-drivenDeviceNodenodepolicy=1;//policy字段为驱动服务的策略release,驱动服务管理章节有详细介绍priority=100;//驱动启动优先级(0-200),值越大优先级越低,建议默认配置100,优先级相同不保证设备的加载顺序preload=0;//驱动加载策略,参考permission=0664;//驱动创建设备节点权限moduleName="sample_driver";//驱动名称,该字段的值必须与驱动入口结构体的moduleName值一致serviceName="sample_service";//驱动发布服务的名称必须唯一";//匹配驱动私有数据的关键字必须等于驱动私有数据配置表中match_attr的值}}}}}定义设备列表时使用HCS模板语法,内容模板下的主机节点由HDF框架定义。新添加的主机和主机中的设备只需要继承模板,填写具体内容即可。配置中定义的设备在加载过程中会生成一个设备实例。在配置中,通过moduleName字段指定设备对应的驱动名称,从而将设备与驱动关联起来。其中,一个设备和一个驱动可能是一对多的关系,即一个驱动可能支持多个相同类型的设备。3.用户态程序与驱动的交互用户态程序与驱动的交互是基于HDFIoService模型实现的。这种设计屏蔽了特定内核的差异,将驱动接口抽象为一个IoService对象。调用者根据名称获取对象,可以使用IoService系列接口进行接口调用和事件监听。值得一提的是,HDFSbuf对象用于消息传递过程中参数的序列化和反序列化,可以避免不受控制的内存访问,简化消息传递和分发过程中的内存所有权问题,有利于提高用户之间数据传递的安全性和便利性模式和内核模式。HDFSbuf相关接口可参考HarmonyOS设备开发官网APIReference中的头文件hdf_sbuf.h。基于HDF框架的用户态程序与驱动交互的代码如下:#defineSAMPLE_WRITE_READ0xFF00intg_intg;staticintOnDevEventReceived(void*priv,uint32_tid,structHdfSBuf*data){constchar*string=HdfSbufReadString(数据);intret=HDF_SUCCESS;if(string==NULL){HDF_LOGE(“failedtoreadstringineventdata”);ret=HDF_seFAILURE;}{HDF_LOGE("%s",string);}g_replyFlag=1;returnret;}staticintSendEvent(structHdfIoService*serv,char*e??ventData){intret=0;structHdfSBuf*data=HdfSBufObtainDefaultSize();//序列化对象被发送如果theapplication(data==NULL){HDF_LOGE("failedtoobtainsbufdata");return1;}structHdfSBuf*reply=HdfSBufObtainDefaultSize();//申请返回数据的序列化对象if(reply==NULL){HDF_LOGE("failedtoobtainsbufreply");ret=HDF_DEV_ERR_NO_MEMORY;gotoout;}if(!HdfSbufWriteString(data,eventData)){//准备m消息内容HDF_LOGE("failedtowritesbuf");ret=HDF_FAILURE;gotoout;}ret=serv->dispatcher->Dispatch(&serv->object,SAMPLE_WRITE_READ,data,reply);//发起接口调用if(ret!=HDF_SUCCESS){HDF_LOGE("failedtosendservicecall");gotoout;}intreplyData=0;if(!HdfSbufReadInt32(reply,&replyData)){//反序列化返回数据);out:HdfSBufRecycle(data);HdfSBufRecycle(reply);returnret;}intmain(){structHdfIoService*serv=HdfIoServiceBind(SAMPLE_SERVICE_NAME);//通过名称获取IoService对象,与驱动配置中的名称一致if(serv==NULL){HDF_LOGE("failedtogetservice%s",SAMPLE_SERVICE_NAME);returnHDF_FAILURE;}staticstructHdfDevEventlistenerlistener={//构造驱动事件监听对象.callBack=OnDevEventReceived,//填充事件处理方法.priv=NULL;};if(HdfDeviceRegisterEventListener(serv,&listener)!=HDF_SUCCESS){//注册事件监听器HDF_LOGE("failedtoregistereventlistener");returnHDF_FAILURE;}if(SendEvent(serv,"他lloWorld,HDFDriver!")){//调用驱动接口,示例驱动接收事件HDF_LOGE("failedtosendevent");returnHDF_FAILURE;}while(g_replyFlag==0){//等待驱动上报事件休眠(1);}HdfDeviceUnregisterEventListener(serv,&listener));//注册事件监听器HdfIoServiceRecycle(serv);//回收IoService对象return0;}示例执行后,字符串“HelloWorld,HDFDriver!”将打印在终端,表示我们的用户态测试程序和驱动成功交互了一次3.使用DevEcoDeviceTool进行驱动开发上一节介绍了OpenHarmony驱动的一般开发方法,那么有没有更简单的添加驱动的方法?答案是华为的南向开发IDE——DevEcoDeviceTool,最新版本的DevEcoDeviceTool已经集成了HDF驱动开发功能,下面介绍如何使用DevEcoDeviceTool进行驱动开发,DevEcoDeviceTool下载链接:https://device.harmonyos.com/cn/develop/ide#download_release。1.创建驱动(1)参考DevEcoDeviceTool手册导入工程,通过npm或网络下载导入OHOS工程。图5DevEcoDeviceTool启动界面(2)使用HDF页面工具创建新的驱动,根据要求填写Module名称,工具会根据Module名称创建对应的驱动代码。图6DeviceEcoToolHDF插件界面DevEcoDeviceTool会自动生成驱动实现代码:图7DeviceEcoTool生成驱动代码并自动生成源代码文件的编译脚本:图8DeviceEcoTool生成驱动编译脚本DevEcoDeviceTool也会在单板的驱动配置中生成相应的Driver配置信息:图9DeviceEcoTool生成驱动配置信息2.修改驱动点击链接修改相关文件,实现驱动功能。DevEcoDeviceTool的自动代码生成已经提供了DriverEntry的基本实现,只需要填写对应函数的实际功能即可。3.编译版本使用DevEcoDeviceTool构建功能一键编译版本,编译输出显示在终端窗口:图11DeviceEcoTool编译界面4.烧录验证DevEcoDeviceTool提供一站式烧录调试环境。使用上传功能将编译好的镜像烧录到开发板中。图12DeviceEcoTool编程功能界面终端窗口显示编程过程和进度。图13DeviceEcoTool编程输出4.总结除了在本次HDC大会上与大家分享驱动框架的设计和最新进展外,OpenAtomFoundation还发布了一系列的技术分享、Guidance文档等资料,欢迎大家关注关注,共建OpenHarmony驱动生态。更多信息请访问:Harmonyos.51cto.com,与华为官方合作打造的鸿蒙技术社区