[经验分享] 【SUBJECT技术】OpenHarmony硬件适配之HCS应用 原创 精华

诚迈_雨哥 显示全部楼层 发表于 2023-12-21 20:00:46

一、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

四、HDF关键点说明

HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理、驱动消息机制和配置管理。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。

4.1. HDF驱动模型

HDF框架将一类设备驱动放在同一个Host里面,开发者也可以将Host中的驱动功能分层独立开发和部署,支持一个驱动多个Node,HDF驱动模型如下图所示:

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仅仅是其中一个功能点,此文旨在抛砖引玉,深层了解还需要阅读专业文档和源码。

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

返回顶部