OpenHarmony开发者论坛
标题:
【SUBJECT技术】OpenHarmony硬件适配之HCS应用
[打印本页]
作者:
诚迈_雨哥
时间:
2023-12-21 20:00
标题:
【SUBJECT技术】OpenHarmony硬件适配之HCS应用
[md]# 一、HCS配置管理
HCS(HDF Configuration Source)是 HDF 驱动框架的配置描述参数文件,内容以 Key-Value 为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。 HC-GEN(HDF Configuration Generator)是 HCS 配置转换工具,可以将 HDF 配置文件转换为软件可读取的文件格式。本文不涉及HC-GEN,假设我们配置好HCS, 利用其给定的接口可以访问对应设备节点的参数。
HCS和硬件板卡直接相关,服务于HDF驱动框架。下面以一个模拟耳机插拔检测的驱动程序为例,从开发的角度逐步展开说明。
# 二、应用举例
## 第一步,找到HDF系统相关总入口hdf.hcs文件,在audio位置新增加audio/analog_headset_config.hcs参数文件,系统在加载的时候会读取该参数文件。
.\vendor\hihope\rk3568\hdf_config\khdf\hdf.hcs
```
#include "device_info/device_info.hcs"
......
#include "audio/audio_config.hcs"
#include "audio/codec_config.hcs"
#include "audio/dai_config.hcs"
#include "audio/dma_config.hcs"
#include "audio/dsp_config.hcs"
#include "audio/analog_headset_config.hcs" // 新增加文件
......
#include "lcd/lcd_config.hcs"
root {
module = "rockchip,rk3568_chip";
}
```
## 第二步,新增加的文件放到对应子模块的目录下
headset_info 就是耳机插拔检测用到的硬件信息,关键是headset_gpio
```
.\vendor\hihope\rk3568\hdf_config\khdf\audio\analog_headset_config.hcs
root {
platform {
template headset_info {
match_attr = "";
serviceName = "";
}
headset :: headset_info {
match_attr = "analog_headset_attr";
serviceName = "analog_headset_service";
vendor = 0x0001;
product = 0x0001;
version = 0x0100;
dev_name = "rk809_analog_headset";
headset_gpio = 115;
headset_gpio_flag = 0;
mic_switch_gpio = 0;
hp_mic_io_value = 0;
main_mic_io_value = 1;
headset_wakeup = 1;
hook_gpio = 0;
adc_controller_no = 0;
adc_channel = 0;
hook_down_type = 0;
}
}
}
```
## 第三步,这个功能需要在audio关键模块加载之后加载
在device_info.hcs中找到audio::host主机节点,配置好服务策略、优先级、加载策略(按时还是按需)、模块名称为驱动加载的名称,服务名称则指向analog_headset_config.hcs中的服务名称。模块名称错误驱动程序将不会被加载,服务名称错误则读取参数失败,可能导致驱动程序异常。
关于服务策略、优先级、加载策略在后面描述。
```
.\vendor\hihope\rk3568\hdf_config\khdf\device_info\device_info.hcs
audio :: host {
hostName = "audio_host";
priority = 110;
device_dai :: device {
device_primary :: deviceNode {
policy = 1;
priority = 50;
preload = 0;
permission = 0666;
moduleName = "DAI_RK3568";
serviceName = "dai_service";
deviceMatchAttr = "hdf_dai_driver";
}
device_hdmi :: deviceNode {
policy = 1;
priority = 50;
preload = 0;
permission = 0666;
moduleName = "DAI_RK3568";
serviceName = "hdmi_dai_service";
deviceMatchAttr = "hdf_hdmi_dai_driver";
}
}
......
// 以下为新增加部分
device_analog_headset :: device {
device0 :: deviceNode {
policy = 1;
priority = 90;
preload = 0;
permission = 0666;
moduleName = "AUDIO_ANALOG_HEADSET";
serviceName = "analog_headset_service";
deviceMatchAttr = "analog_headset_attr";
}
}
}
```
## 第四步,驱动程序通过 HDF_INIT宏,在系统启动时完成设备与服务的绑定,关键就是这个模块名称,找到这个模块名称后,进而去初始化硬件。
.\device\board\hihope\rk3568\audio_drivers\headset_monitor\src\analog_headset_core.c
```
/* HdfDriverEntry definitions */
struct HdfDriverEntry g_headsetDevEntry = {
.moduleVersion = 1,
.moduleName = "AUDIO_ANALOG_HEADSET",
.Bind = HdfHeadsetBindDriver,
.Init = HdfHeadsetInit,
.Release = HdfHeadsetExit,
};
HDF_INIT(g_headsetDevEntry);
```
## 第五步,初始硬件时,务必先读取必要的HCS硬件信息
```
static int32_t HdfHeadsetInit(struct HdfDeviceObject *device)
{
const struct DeviceResourceNode *node = NULL;
static struct HeadsetPdata pdata;
......
node = device->property;
ret = ReadConfig(node, &pdata);
......
return HDF_SUCCESS;
}
```
## 第六步,调用 device_resource_if.h 中对外的接口函数,根据名称读取所有信息到pdata
```
#include "device_resource_if.h"
static int32_t ReadConfig(const struct DeviceResourceNode *node, struct HeadsetPdata *pdata)
{
int32_t ret;
int32_t temp;
struct DeviceResourceIface *parser = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
ret = parser->GetString(node, "dev_name", &pdata->devName, NULL);
ret = parser->GetUint32(node, "headset_gpio", &pdata->hsGpio, 0);
ret = parser->GetUint32(node, "headset_gpio_flag", &pdata->hsGpioFlag, OF_GPIO_ACTIVE_LOW);
ret = ReadHookModeConfig(parser, node, pdata);
(void)ReadMicConfig(parser, node, pdata);
ret = parser->GetUint32(node, "headset_wakeup", &temp, 0);
return HDF_SUCCESS;
}
```
## 第七步,驱动编写完成后,需要就近修改Makefile中要编译的驱动源文件
.\device\board\hihope\rk3568\audio_drivers\Makefile
```
obj-$(CONFIG_DRIVERS_HDF_AUDIO_ANA_HEADSET) += \
headset_monitor/src/analog_headset_base.o \
headset_monitor/src/analog_headset_core.o \
headset_monitor/src/analog_headset_gpio.o \
headset_monitor/src/analog_headset_adc.o
ccflags-$(CONFIG_DRIVERS_HDF_AUDIO_ANA_HEADSET) += \
-I$(srctree)/$(KHDF_FRAMEWORK_ROOT_DIR)/model/input/driver \
-I$(srctree)/drivers/hdf/evdev \
-I$(srctree)/$(KHDF_AUDIO_RK3568_INC_DIR)/headset_monitor/include
```
## 第八步,这个属于通用功能,板卡支持与否,需要在Kconfig文件配置CONFIG_DRIVERS_HDF_AUDIO_ANA_HEADSET 来决定是否编译以上文件。
# 三、 device_resource_if.h接口定义
在驱动实现中,使用 device_resource_if.h 中定义的接口对配置进行查询和读取,所在目录.\drivers\hdf_core\interfaces\inner_api\utils\device_resource_if.h。常用 API 介绍如下:
![interface.png](
https://forums-obs.openharmony.c ... cccmq3crtmr28or.png
"interface.png")
# 四、HDF关键点说明
HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理、驱动消息机制和配置管理。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。
## 4.1. HDF驱动模型
HDF框架将一类设备驱动放在同一个Host里面,开发者也可以将Host中的驱动功能分层独立开发和部署,支持一个驱动多个Node,HDF驱动模型如下图所示:
![HCS-tree.png](
https://forums-obs.openharmony.c ... fimvbggvelb8vsy.png
"HCS-tree.png")
开发者应当将同一类的设备放在同一个Host里面,在新增设备时,检查是否已经存在同类型的Host。如果已存在Host,则将Device配置在此Host中,禁止重复配置Host。一个驱动设备应该只属于一类驱动类型,因此也禁止将同一个Device配置在不同Host当中。
## 4.2. 发布策略
驱动服务必须按照业务规则设置对外发布的策略,禁止设置不必要的发布策略。驱动服务是HDF驱动设备对外提供能力的对象,由HDF框架统一管理。HDF框架定义了驱动对外发布服务的策略,是由配置文件中的policy字段来控制,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;
```
因此,驱动服务应该按照业务规则来设置发布策略,禁止设置不必要的发布策略,如内核态驱动设置用户态的发布策略。
## 4.3. 加载时机
HDF驱动加载包括按需加载和按序加载。
按需加载:HDF框架支持驱动在系统启动过程中默认加载,或者在系统启动之后动态加载。
按序加载:HDF框架支持驱动在系统启动的过程中按照驱动的优先级进行加载。
# 五、总结
HDF 框架结构庞杂,意义深远,值得每个底层开发者不断探索和改善。HCS仅仅是其中一个功能点,此文旨在抛砖引玉,深层了解还需要阅读专业文档和源码。
[/md]
欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/)
Powered by Discuz! X3.5