本帖最后由 数码小叶 于 2022-4-19 08:26 编辑
HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。
基于HDF框架进行驱动的开发主要分为两个部分,驱动实现和驱动配置,驱动实现包含驱动业务代码和驱动入口注册,这两者开发文档里都提供了模板,比如驱动业务代码,一个简单的模板如下:
#include"hdf_device_desc.h"// HDF框架对驱动开放相关能力接口的头文件
#include"hdf_log.h"// HDF 框架提供的日志接口头文件
#define HDF_LOG_TAG sample_driver // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签
//驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
int32_t HdfSampleDriverBind(structHdfDeviceObject*deviceObject)
{
HDF_LOGD("Sample driver bind success");
return0;
}
// 驱动自身业务初始的接口
int32_t HdfSampleDriverInit(structHdfDeviceObject*deviceObject)
{
HDF_LOGD("Sample driver Init success");
return0;
}
// 驱动资源释放的接口
void HdfSampleDriverRelease(structHdfDeviceObject*deviceObject)
{
HDF_LOGD("Sample driver release success");
return;
}
实际编程时,就可以直接在此模板上扩展,比如BearPi-HM Micro提供的demo
//驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject)
{
if (deviceObject == NULL)
{
HDF_LOGE("Led driver bind failed!");
return HDF_ERR_INVALID_OBJECT;
}
static struct IDeviceIoService ledDriver = {
.Dispatch = LedDriverDispatch,
};
deviceObject->service = (struct IDeviceIoService *)(&ledDriver);
HDF_LOGD("Led driver bind success");
return HDF_SUCCESS;
}
驱动注册,就是实例化驱动入口,驱动入口必须为HdfDriverEntry(在 hdf_device_desc.h 中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
struct HdfDriverEntry g_ledDriverEntry = {
.moduleVersion = 1,
.Bind = HdfLedDriverBind, //GPIO不需要实现Bind,本例是一个空实现,厂商可根据自身需要添加相关操作
.Init = HdfLedDriverInit, //见Init参考
.Release = HdfLedDriverRelease, //见Release参考
.moduleName = "HDF_LED",,//【必要且需要与HCS文件中里面的moduleName匹配】
};
//调用HDF_INIT将驱动入口注册到HDF框架中
HDF_INIT(g_ledDriverEntry);
而对于驱动配置,HDF框架使用的是.HCS文件,.HCS文件全称是HDF Configuration Source,是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。HC-GEN(HDF Configuration Generator)是HCS配置转换工具,可以将HDF配置文件转换为软件可读取的文件格式:在弱性能环境中,转换为配置树源码或配置树宏定义,驱动可直接调用C代码或宏式APIs获取配置。在高性能环境中,转换为HCB(HDF Configuration Binary)二进制文件,驱动可使用HDF框架提供的配置解析接口获取配置。HCS经过HC-GEN编译生成HCB文件,HDF驱动框架中的HCS Parser模块会从HCB文件中重建配置树,HDF驱动模块使用HCS Parser提供的配置读取接口获取配置内容。
和驱动实现一样,驱动配置同样包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息,HDF框架定义的驱动设备描述位于:~\bearpi\project\bearpi-hm_micro_small\device\st\bearpi_hm_micro\liteos_a\hdf_config\device_info\device_info.hcs。驱动的私有配置信息,则是用户自己添加的配置信息,比如这里的~\bearpi\project\bearpi-hm_micro_small\device\st\bearpi_hm_micro\liteos_a\hdf_config\led\led_config.hcs
device_led :: device { // led设备节点
device0 :: deviceNode { // led驱动的DeviceNode节点
policy = 2; // policy字段是驱动服务发布的策略,等于0,不需要发布服务
priority = 10; // 驱动启动优先级(0-200),值越大优先级越低,
preload = 1; // 驱动按需加载字段
permission = 0777; // 驱动创建设备节点权限
moduleName = "HDF_LED"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
serviceName = "hdf_led"; // 驱动对外发布服务的名称,必须唯一
deviceMatchAttr = "st_stm32mp157_led"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等
}
}
root {
LedDriverConfig {
led_gpio_num = 13;
match_attr = "st_stm32mp157_led"; //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致
}
}
最后,是需要将需要将该配置文件添加到板级配置入口文件hdf.hcs
device\st\bearpi_hm_micro\liteos_a\hdf_config\hdf.hcs
#include "device_info/device_info.hcs"
#include "led/led_config.hcs"
关于GPIO口的操作,GPIO驱动已经提供了相关的接口
然后就是led闪烁的业务代码编写了,就是驱动程序指令接受的的代码,用户态向内核态发送指令
int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
{
uint8_t contrl;
HDF_LOGE("Led driver dispatch");
if (client == NULL || client->device == NULL)
{
HDF_LOGE("Led driver device is NULL");
return HDF_ERR_INVALID_OBJECT;
}
switch (cmdCode)
{
/* 接收到用户态发来的LED_WRITE_READ命令 */
case LED_WRITE_READ:
/* 读取data里的数据,赋值给contrl */
HdfSbufReadUint8(data,&contrl);
switch (contrl)
{
/* 开灯 */
case LED_ON:
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_LOW);
status = 1;
break;
/* 关灯 */
case LED_OFF:
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_HIGH);
status = 0;
break;
/* 状态翻转 */
case LED_TOGGLE:
if(status == 0)
{
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_LOW);
status = 1;
}
else
{
GpioWrite(g_Stm32Mp1ILed.gpioNum, GPIO_VAL_HIGH);
status = 0;
}
break;
default:
break;
}
/* 把LED的状态值写入reply, 可被带至用户程序 */
if (!HdfSbufWriteInt32(reply, status))
{
HDF_LOGE("replay is fail");
return HDF_FAILURE;
}
break;
default:
break;
}
return HDF_SUCCESS;
}
通过代码可以感受到OpenHarmony的设备开发中的驱动调用与单片机的驱动开发不太相同,单片机的驱动调用往往是采用函数调用的方式,但是OpenHarmony没有这样的操作,驱动调用采用的是驱动程序向用户态暴露server后,程序才能通过Dispatch的方式发送指令到驱动程序,并可以将用户态的数据携带到驱动程序,也可以从驱动程序读出数据。像上面的代码就是通过Dispatch向驱动程序发送指令从而实现LED灯的亮灭,当然,内核态也可以向用户态发送数据。
和上一篇,helloword一样,还需要编译文件,对于业务代码,和上一篇一模一样的操作,配置BUILD.gn、applications.json、config.json。对于驱动文件,不但需要自己写的BUILD.gn,还需要修改~\bearpi\project\bearpi-hm_micro_small\device\st\drivers\BUILD.gn
import("//drivers/adapter/khdf/liteos/hdf.gni")
hdf_driver("hdf_led") {
sources = [
"led.c",
]
}
最后编译,烧录到板子了,和之前一样,打开串口助手 ,通过指令操作led的亮灭状态变化