> 技术文档 > OpenHarmony-简单的HDF驱动_openharmony hdf

OpenHarmony-简单的HDF驱动_openharmony hdf

学习于:https://docs.openharmony.cn/pages/v5.0/zh-cn/device-dev/driver/driver-hdf-manage.md
首先,OpenHarmony系统里的HDF(Hardware Driver Foundation)驱动框架,已经规范设备驱动的模型、设备节点的配置与统一的HDI(Hardware Device Interface)驱动调用接口等等。
每个设备驱动都是由一个struct HdfDriverEntry对象来描述,如:

static struct HdfDriverEntry myTestEntry = { .moduleVersion = 1, //指定驱动源码版本 .moduleName = \"myTestDriver\", //驱动模块名,.hcs文件必须有设备节点使用同名都会匹配 .Bind = myBind, //指定驱动模块与设备节点匹配上后调用myBind函数,可绑定向应用程序提供HDI应用程序调用驱动的服务 .Init = myInit, //匹配后,触发调用myInit函数完成初始化工作 .Release = myRelease, //myInit初始化函数执行失败时触发调用myRelease函数释放资源};//通过HDF_INIT宏将结构体变量存储在系统镜像的\".hdf.driver\"段区,在系统启动时在取出此结构体变量进行注册至HDF框架中。HDF_INIT(myTestEntry);

接下来通过开发一个简单的HDF驱动案例来学习,主要步骤过程:
1. HDF设备驱动的开发
这里为了省事,编写出的设备驱动源文件寄生于系统现存的驱动编译管理中。如在OpenHarmony系统源码drivers/hdf_core/adapter/khdf/liteos/platform/src/目录下创建myTestDriver.c,并在文件输入代码如下:

#include \"hdf_device_desc.h\" // HDF框架对驱动开发相关能力接口的头文件#include \"hdf_log.h\"  // HDF框架提供的日志接口头文件#include \"hdf_base.h\"  // HDI驱动调用接口头文件int32_t myBind(struct HdfDeviceObject *obj){ HDF_LOGD(\"#### in %s\\n\", __func__); return HDF_SUCCESS;}int32_t myInit(struct HdfDeviceObject *obj){ HDF_LOGD(\"#### in %s\\n\", __func__); return HDF_SUCCESS;}void myRelease(struct HdfDeviceObject *obj){ HDF_LOGD(\"#### in %s\\n\", __func__);}struct HdfDriverEntry myTestEntry = { .Bind = myBind, .Init = myInit, .Release = myRelease, .moduleName = \"myTestDriver\", .moduleVersion = 1};HDF_INIT(myTestEntry);

为了将myTestDriver.c源文件编译进系统镜像,修改drivers/hdf_core/adapter/khdf/liteos/platform目录下的BUILD.gn,在42行增加以下内容:

 42 \"src/myTestDriver.c\"

修改drivers/hdf_core/adapter/khdf/liteos/platform目录下的Makefile,在37行增加内容如图所示:
OpenHarmony-简单的HDF驱动_openharmony hdf

完成后就可以编译OpenHarmony系统,编译完成后在out输出目录下找到相应的.o文件则表示驱动源文件已成功编译,如图所示:
OpenHarmony-简单的HDF驱动_openharmony hdf
但这时先不急着烧录更新系统镜像,因驱动并不是编译进系统镜像就表示产生作用的,设备驱动还需要在.hcs文件中有同样moduleName的设备节点描述,匹配上后驱动才会启动。

2. HCS设备节点描述
OpenHarmony官方文档对hcs语法提供了非常详细的指导,这里不表。匹配设备驱动的设备节点描述如下:

 myTestHost :: host{ //继承template模板host,表示某种类型的设备,host名必须唯一 hostName = \"myTestHost\"; // host名称 priority = 100; // host启动优先级(0-200),值越大优先级越低 testDevice :: device { //testDevice设备节点继承于template模板device device0 :: deviceNode { //节点device0继承于template模板deviceNode,表示具体一个设备对象,设备名不能重复  policy = 2; // policy字段是驱动服务发布的策略,2表示向用户态提供服务接口  priority = 100; // 驱动启动优先级(0-200),值越大优先级越低  preload = 0; //驱动在系统启动时加载。  permission = 0664; //驱动创建设备节点权限  moduleName = \"myTestDriver\"; //此名必须与设备驱动的moduleName一致才会匹配。可以有多个设备节点使用同样的moduleName  serviceName = \"myTestService\";//驱动对外发布服务的名称,应用程序通过此名获取设备驱动提供的服务,从而交互操作,所以此服务名必须唯一。 } } }

下面可选使用模拟设备或Hi3516开发板进行验证:
1). 如使用OpenHarmony的qemu模拟设备,则在device/qemu/arm_virt/liteos_a/hdf_config/device_info/device_info.hcs尾部增加设备节点描述,如图所示:
OpenHarmony-简单的HDF驱动_openharmony hdf
完成后编译系统,启动虚拟设备后,执行命令“hilogcat\",即可查看到设备驱动函数的输出信息如图所示:
OpenHarmony-简单的HDF驱动_openharmony hdf
2). 如使用Hi3516开发板,则在vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs尾部增加设备节点描述,如图所示:
OpenHarmony-简单的HDF驱动_openharmony hdf
这里注意即使编译并更新使用新系统镜像也无法查看到设备驱动输出信息的,因系统在启动时通过uart接口输出hilog信息,并且uart接口速率无法及时输出大量信息,导致发生多次\"hilog ringbuffer full, drop 4 line(s) log\"爆缓冲区的状况,所以无法像模拟设备一样查看到设备驱动函数的输出。

3. 设备增加HDI调用接口服务
修改myTestDriver.c驱动源文件内容,增加HDI调用接口服务函数,增加与修改内容如下:

//增加myDispatch函数,在应用程序通过服务Dispatch时触发调用static int32_t myDispatch( struct HdfDeviceIoClient *client, int id, struct HdfSBuf *data, struct HdfSBuf *reply){ HDF_LOGD(\"### in %s : %d\\n\", __func__, id); return HDF_SUCCESS;}int32_t myBind(struct HdfDeviceObject *obj) //修改{ //每个struct IDeviceIoService对象表示一个HDI调用服务对象 static struct IDeviceIoService testService = { .Dispatch = myDispatch, //指定由myDispatch函数处理服务 }; obj->service = &testService; //指定设备obj使用testService服务对象 HDF_LOGD(\"#### in %s\\n\", __func__); return HDF_SUCCESS;}

4. 应用程序通过HDI调用接口访问设备驱动
编写应用程序,实现通过hcs设备节点描述的moduleName获取设备驱动服务,并传递参数。源文件mytest.c内容如下:

#include #include #include #include #include \"hdf_log.h\"#include \"hdf_sbuf.h\"#include \"hdf_io_service_if.h\"int main(void){ //通过moduleName获取设备驱动的服务对象 struct HdfIoService *serv = HdfIoServiceBind(\"myTestService\"); if (serv == NULL) { printf(\"fail to get service\\n\"); return 1; } for (int n = 0; n < 20; n++) { //向设备驱动服务传递参数,触发调用驱动的myDispatch函数 int ret = serv->dispatcher->Dispatch(&serv->object, n, NULL, NULL); if (ret < 0) break; sleep(5); } printf(\"test end\\n\"); return 0;}

应用程序编译的BUILD.gn内容如下:

hdf_framework_path = \"../../../../../framework\"executable(\"mytest\") { sources = [ \"mytest.c\" ] include_dirs = [ \"$hdf_framework_path/ability/sbuf/include\", \"$hdf_framework_path/core/shared/include\", \"$hdf_framework_path/core/host/include\", \"$hdf_framework_path/core/master/include\", \"$hdf_framework_path/include/core\", \"$hdf_framework_path/include/utils\", \"$hdf_framework_path/utils/include\", \"$hdf_framework_path/include/osal\", \"//third_party/bounds_checking_function/include\", \"//base/hiviewdfx/hilog_lite/interfaces/native/innerkits\", ] deps = [ \"//drivers/hdf_core/adapter/uhdf/manager:hdf_core\", \"//drivers/hdf_core/adapter/uhdf/posix:hdf_posix_osal\", \"//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared\", ] public_deps = [ \"//third_party/bounds_checking_function:libsec_shared\" ] defines = [ \"__USER__\" ]}

5. 应用程序运行效果
编译并更新使用新系统镜像后,执行bin目录下的mytest程序,输出信息如图所示:
OpenHarmony-简单的HDF驱动_openharmony hdf