# HDF_framework **Repository Path**: biermoe/hdf_framework ## Basic Information - **Project Name**: HDF_framework - **Description**: hdf_source - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-03-12 - **Last Updated**: 2024-03-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 概述 OpenHarmony LiteOS-A 内核是基于 Huawei LiteOS 内核演进发展的新一代内核,Huawei LiteOS 是面向 IoT 领域构建的轻量级物联网操作系统。在 IoT 产业高速发展的潮流中,OpenHarmony LiteOS-A 内核能够带给用户小体积、低功耗、高性能的体验以及统一开放的生态系统能力,新增了丰富的内核机制、更加全面的 POSIX 标准接口以及统一驱动框架 HDF(OpenHarmony Driver Foundation)等,为设备厂商提供了更统一的接入方式,为 OpenHarmony 的应用开发者提供了更友好的开发体验。 OpenHarmony系统HDF驱动框架采用C语言面向对象编程模型构建,通过平台解耦、内核解耦,来达到兼容不同内核,统一平台底座的目的,从而帮助开发者实现驱动一次开发,多系统部署的效果。 ## 1.HDF 驱动框架 OpenHarmony 系统 HDF 驱动框架主要由驱动基础框架、驱动程序、驱动配置文件和驱动接口这四个部分组成。 1)HDF 驱动基础框架提供统一的硬件资源管理,驱动加载管理以及设备节点管理等功能。驱动框架采用的是主从模式设计,由 Device Manager 和 Device Host 组成。Device Manager 提供了统一的驱动管理,Device Manager 启动时根据 Device Information 提供驱动设备信息加载相应的驱动 Device Host,并控制 Host 完成驱动的加载。Device Host 提供驱动运行的环境,同时预置 Host Framework 与 Device Manager 进行协同,完成驱动加载和调用。根据业务的需求 Device Host 可以有多个实例。 2)驱动程序实现驱动具体的功能,每个驱动由一个或者多个驱动程序组成,每个驱动程序都对应着一个 Driver Entry。Driver Entry 主要完成驱动的初始化和驱动接口绑定功能。 3)驱动配置文件.hcs 主要由设备信息(Device Information)和设备资源(Device Resource)组成。Device Information 完成设备信息的配置。如配置接口发布策略,驱动加载的方式等。Device Resource 完成设备资源的配置。如 GPIO 管脚、寄存器等资源信息的配置。 4)驱动接口 HDI\(Hardware Driver interface \)提供标准化的接口定义和实现,驱动框架提供 IO Service和IO Dispatcher 机制,使得不同部署形态下驱动接口趋于形式一致。 ![HDF](https://gitee.com/biermoe/hdf_framework/raw/master/png/HDF%E9%A9%B1%E5%8A%A8%E6%A1%86%E6%9E%B6.png?x-oss-process=image/resize,w_820,h_609 "HDF") HDF框架以组件化的驱动模型作为核心设计思路,为开发者提供更精细化的驱动管理,让驱动开发和部署更加规范。HDF框架将一类设备驱动放在同一个host里面,开发者也可以将驱动功能分层独立开发和部署,支持一个驱动多个node,HDF驱动模型如下图所示: ![HDF](https://gitee.com/biermoe/hdf_framework/raw/master/png/HDF%E9%A9%B1%E5%8A%A8%E6%A8%A1%E5%9E%8B.png?x-oss-process=image/resize,w_654,h_456 "HDF") ## 2.HDF 驱动开发 基于HDF框架进行驱动的开发主要分为两个部分,驱动实现和驱动配置,详细开发流程如下所示: ### 2.1 驱动实现 驱动实现包含驱动业务代码和驱动入口注册。 #### 2.1.1 驱动业务代码 //驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架 ``` int HdfMotorDriverBind(struct HdfDeviceObject *device) //绑定 { if (NULL == tester) tester = (struct MotorTester *)OsalMemCalloc(sizeof(*tester)); //为结构体申请内存 HDF_LOGE("%s::motor-enter", __func__); if (device != NULL) { device->service = &(tester->service); //绑定Service到结构体中 device->service->Dispatch = motorDriverDispatch; //绑定Dispatcher到结构体中 用于用户态消息传递 } else { HDF_LOGE("%s: device is NULL", __func__); } HDF_LOGE("%s: bind success", __func__); return HDF_SUCCESS; } ``` // 驱动自身业务初始的接口 ``` int HdfMotorDriverInit(struct HdfDeviceObject *device) //驱动的初始化 { HDF_LOGE("%s::motor-enter", __func__); //初始化创建任务的参数 TSK_INIT_PARAM_S initParam = {0}; initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)HdfMotorTask; //入口函数 initParam.usTaskPrio = 4; //任务优先级 initParam.pcName = "MOTOR"; //任务名 initParam.uwStackSize = LOS_TASK_MIN_STACK_SIZE; //栈大小 } ``` // 驱动资源释放的接口 ``` void HdfMotorDriverRelease(struct HdfDeviceObject *device) //驱动资源释放 { HDF_LOGE("%s::motor-enter", __func__); //各类资源释放 device->service = NULL; HDF_LOGE("%s: success", __func__); } ``` #### 2.1.2驱动入口注册到HDF框架 // 定义驱动入口的对象,必须为HdfDriverEntry(在hdf\_device\_desc.h中定义)类型的全局变量 ``` struct HdfDriverEntry g_motorDriverEntry = { //驱动入口 .moduleVersion = 1, .moduleName = "HDF_MOTOR_DRIVER", //驱动模块名称 .Bind = HdfMotorDriverBind, //驱动绑定 .Init = HdfMotorDriverInit, //驱动初始化 .Release = HdfMotorDriverRelease, //驱动释放 }; ``` HDF\_INIT\(g\_motorDriverEntry\); // 调用HDF\_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 ### 2.2 驱动配置 驱动配置包含两部分,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提供的配置读取接口获取配置内容。驱动配置过程的原理图如下所示: ![HDF](https://gitee.com/biermoe/hdf_framework/raw/master/png/%E9%A9%B1%E5%8A%A8%E9%85%8D%E7%BD%AE%E8%BF%87%E7%A8%8B%E7%9A%84%E5%8E%9F%E7%90%86%E5%9B%BE.png?x-oss-process=image/resize,w_820,h_388 "HDF") #### 2.2.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 { //MOTOR设备节点。 device0 :: deviceNode { //MOTOR驱动的DeviceNode节点。 policy = 2; //驱动服务发布的策略 priority = 10; //驱动启动优先级(0-200),值越大优先级越低,建议默认配 100,优先级相同则不保证 device 的加载顺序 preload = 0; //驱动按需加载字段 permission = 0664;//驱动创建设备节点权限 moduleName = "HDF_MOTOR_DRIVER"; //驱动名称,该字段的值必须和驱动入口结构的moduleName值一致 serviceName = "HDF_MOTOR_SERVICE"; //驱动对外发布服务的名称,必须唯一 deviceMatchAttr = "driver_config";//驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等。 } } } ``` 其中 Policy 参数解释: ``` typedef enum { /* 驱动不提供服务 */ SERVICE_POLICY_NONE = 0, /* 驱动对内核态发布服务 */ SERVICE_POLICY_PUBLIC = 1, /* 驱动对内核态和用户态都发布服务 */ SERVICE_POLICY_CAPACITY = 2, /* 驱动服务不对外发布服务,但可以被订阅 */ SERVICE_POLICY_FRIENDLY = 3, /* 驱动私有服务不对外发布服务,也不能被订阅 */ SERVICE_POLICY_PRIVATE = 4, /* 错误的服务策略 */ SERVICE_POLICY_INVALID } ServicePolicy ``` ## 3.HDF 驱动加载 HDF驱动加载包括按需加载和按序加载。按需加载是HDF框架支持驱动在系统启动过程中默认加载,或者在系统启动之后动态加载;按序加载是HDF框架支持驱动在系统启动的过程中按照驱动的优先级进行加载。HDF框架定义的驱动按需加载方式的策略是由配置文件中的 preload 字段来控制,preload 字段的取值范围以及含义如下: ![HDF](https://gitee.com/biermoe/hdf_framework/raw/master/png/preload%20%E5%AD%97%E6%AE%B5.png?x-oss-process=image/resize,w_820,h_422 "HDF") 驱动的按序加载是通过配置文件中的 priority(取值范围为整数 0 到 200)来决定的,priority 值越小,表示的优先级越高。驱动的加载顺序,优先根据 host 的 priority 决定,如果host 的 priority 相同,再根据 host 内的驱动 priority 值来决定加载顺序。 ### 3.1 HDF\_INIT宏展开 驱动入口注册到HDF框架,会调用HDF\_INIT函数将驱动入口地址注册到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\)\) ![HDF_INIT宏展开](https://gitee.com/biermoe/hdf_framework/raw/master/png/HDF_INIT%E5%AE%8F%E5%B1%95%E5%BC%80.png?x-oss-process=image/resize,w_820,h_363 "HDF") 可以看到 HDF\_INIT 宏是定义了一个“驱动模块名+HdfEntry”的符号放到".hdf.driver"所在 section,该符号指向的内存地址即为驱动程序入口结构体的地址。这个特殊的 section 将用于开机启动时查找设备驱动。 ### 3.2获取驱动列表 ![HDF](https://gitee.com/biermoe/hdf_framework/raw/master/png/%E9%A9%B1%E5%8A%A8%E5%88%97%E8%A1%A8.png?x-oss-process=image/resize,w_820,h_372 "HDF") HDF驱动框架通过将驱动程序入口符号的地址集中存放到一个特殊的 section 来实现对驱动的索引,这个section的开头和末尾插入了\_hdf\_drivers\_start、\_hdf\_drivers\_end两个特殊符号,用于标记这个 section 的范围,两个特殊符号之间的数据即为驱动实现指针。 ### 3.3获取设备列表 ![HDF](https://gitee.com/biermoe/hdf_framework/raw/master/png/%E8%8E%B7%E5%8F%96%E8%AE%BE%E5%A4%87%E5%88%97%E8%A1%A8.png?x-oss-process=image/resize,w_820,h_328 "HDF") 配置文本编译后会变成二进制格式的配置文件,其中设备相关信息被存放在一个用“hdf\_manager”标记的 device\_info 配置块中,host的内容以块的形式在device\_info 块中依次排列,host块中记录了host名称、启动优先级和设备列表信息。设备信息中的 moduleName字段将用于和驱动程序入口中的moduleName进行匹配,从而为设备匹配到正确的驱动程序,完成设备与驱动的匹配。 ## 4.HDF 驱动调用与消息传递 ### 4.1 内核态Dispatcher实现 驱动绑定时,将Dispatcher绑定到Bind中的结构体中,用于响应用户态消息传递及功能实现。 ``` int32_t motorDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, struct HdfSBuf *reply) { HDF_LOGE("%s::motor-enter\n", __func__); int32_t ret = HDF_SUCCESS; int32_t result = HDF_SUCCESS; // switch (cmdId) { case MOTOR_START: break; case MOTOR_STOP: break; default: break; } return ret; } ``` ### 4.1 用户态Dispatcher调用 用户态在成功初始化绑定驱动服务节点后,即可调用Dispatcher接口 ``` static int SendEvent(struct HdfIoService *serv, char *eventData) { int ret = 0; struct HdfSBuf *data = HdfSBufObtainDefaultSize(); //为data申请内存空间 if (data == NULL) { HDF_LOGE("fail to obtain sbuf data"); return 1; } struct HdfSBuf *reply = HdfSBufObtainDefaultSize(); //为reply申请内存空间 if (reply == NULL) { HDF_LOGE("fail to obtain sbuf reply"); ret = HDF_DEV_ERR_NO_MEMORY; goto out; } if (!HdfSbufWriteString(data, eventData)) { //将参数写入data HDF_LOGE("fail to write sbuf"); ret = HDF_FAILURE; goto out; } int m = atoi(eventData); //对参数进行判断,决定要对Dispatch传入的cmdId if (m != 0) { serv->dispatcher->Dispatch(&serv->object, MOTOR_START, data, reply); } else { serv->dispatcher->Dispatch(&serv->object, MOTOR_STOP, data, reply); } out: HdfSBufRecycle(data); HdfSBufRecycle(reply); return ret; } ``` 用户态驱动初始化 ``` int main(int argc, char *argv[]) //argc表示传入参数个数 { struct HdfIoService *motor = HdfIoServiceBind(MOTOR_SERVICE_NAME); //根据服务名绑定驱动 if (motor == NULL) { HDF_LOGE("fail to get service %s", MOTOR_SERVICE_NAME); return HDF_FAILURE; } HDF_LOGE("success to get service %s", MOTOR_SERVICE_NAME); SendEvent(motor, "0"); } ``` ## 5总结 1)在系统启动时,DeviceManagerInit通过late\_initcall先启动。 2) Device Manager 根据 Device Information 信息,解析配置文件中的 Host 列表,根据 Host 列表中的信息来实例化对应的 Host 对象。 3)Host遍历设备列表去获取与之匹配的驱动程序名称,然后基于驱动程序名称遍历.hdf.driver section 获得驱动程序地址。 4)设备与驱动匹配成功之后,获取指定驱动的入口地址,加载对应的设备驱动程序。 5)调用指定驱动的 Bind 接口,用于关联设备和服务实例。 6)调用指定驱动的 Init 接口,用于完成驱动的相关初始化工作。 7)如果驱动被卸载或者因为硬件等原因 Init 接口返回失败,Release 将被调用,用于释放驱动申请的各类资源。