# input设备适配HDF框架并和用户态程序交互  #

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

input设备适配HDF框架

以rk3568中 gt911_5p5 触摸屏为例说明input设备适配hdf框架步骤:

1、添加驱动配置文件

1.1 配置设备驱动描述信息【必须】

配置文件路径:
./vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs

在该文件的input::host{}中添加对应的设备描述:

   input :: host {
        hostName = "input_host";
        priority = 100;
        //公共驱动:HDF_INPUT_MANAGER
        device_input_manager :: device {
            ...
        }
        //公共驱动:HDF_TOUCH
        device_hdf_touch :: device {
            ...
        }
        //适配gt911_5p5触摸屏驱动描述信息
        device_touch_chip :: device {//设备节点
            device0 :: deviceNode {//驱动的DeviceNode节点。
                policy = 0;//驱动服务发布的策略
                priority = 130;//驱动启动优先级(0-200),值越大优先级越低,
                preload = 0;//驱动按需加载字段,0:默认加载,1:当系统支持快速启动的时候,则在系统完成之后再加载这一类驱动 2:默认不加载,支持后续动态加载
                permission = 0660;//驱动创建设备节点权限
                moduleName = "HDF_TOUCH_GT911";//驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
                serviceName = "hdf_touch_gt911_service";//驱动对外发布服务的名称,必须唯一
                deviceMatchAttr = "zsj_gt911_5p5";//驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等。
            }
        }
        //公共驱动:HDF_HID
        device_hdf_hid :: device {
            ...
        }
        //公共驱动:HDF_INFRARED
        device_hdf_infrared :: device {
            ...
        }
}

1.2 配置设备私有描述信息:【可选】

如果驱动配置有deviceMatchAttr参数,需添加一个私有配置文件:

路径:./vendor/hihope/rk3568/hdf_config/khdf/device_info/input_config.hcs

root {
input_config {
    touchConfig {
        touch0 {
            //公共驱动:HDF_TOUCH 私有配置信息
            boardConfig {
                match_attr = "touch_device1";
                inputAttr {
                    /* 0:touch 1:key 2:keyboard 3:mouse 4:button 5:crown 6:encoder */
                    inputType = 0;
                    solutionX = 720;
                    solutionY = 1280;
                    devName = "main_touch";
                }
                //部分公共touch私有配置信息跟随触摸器件修改
                // Hi3516DV300-Runhe  gt911--5p5 & 4p0 start
                busConfig {
                    // 0:i2c 1:spi
                    busType = 0;
                    busNum = 1;
                    clkGpio = 86;
                    dataGpio = 87;
                    i2cClkIomux = [0x114f0048, 0x403];
                    i2cDataIomux = [0x114f004c, 0x403];
                }

                pinConfig {
                    rstGpio = 14;
                    intGpio = 13;
                    rstRegCfg = [0x112f0094, 0x400];
                    intRegCfg = [0x112f0098, 0x400];
                }
                // Hi3516DV300-Runhe  gt911--5p5 & 4p0 end
                powerConfig {
                    /* 0:unused 1:ldo 2:gpio 3:pmic */
                    vccType = 2;
                    vccNum = 20;    // gpio20
                    vccValue = 1800;
                    vciType = 1;
                    vciNum = 12;    // ldo12
                    vciValue = 3300;
                }

                featureConfig {
                    capacitanceTest = 0;
                    gestureMode = 0;
                    gloverMOde = 0;
                    coverMode = 0;
                    chargerMode = 0;
                    knuckleMode = 0;
                }
            }

            chipConfig {
                //适配gt911_5p5触摸屏私有驱动描述信息:zsj_gt911_5p5
                chip0 :: touchChip {
                    match_attr = "zsj_gt911_5p5";
                    chipInfo = "ZIDN45100";  // 4-ProjectName, 2-TP IC, 3-TP Module
                    chipVersion = 0; //parse point by TypeA                 
                    chipName = "gt911";
                    vendorName = "zsj";
                    /* 0:i2c 1:spi*/
                    busType = 0;
                    deviceAddr = 0x5D;
                    /* 0:None 1:Rising 2:Failing 4:High-level 8:Low-level */
                    irqFlag = 2;
                    maxSpeed = 400;
                    chipVersion = 0; //parse Coord TypeA
                    powerSequence {
                        /* [type, status, dir , delay]
                            <type> 0:none 1:vcc-1.8v 2:vci-3.3v 3:reset 4:int
                            <status> 0:off or low  1:on or high  2:no ops
                            <dir> 0:input  1:output  2:no ops
                            <delay> meanings delay xms, 20: delay 20ms
                         */
                        powerOnSeq = [4, 0, 1, 5,
                                     3, 0, 1, 10,
                                     3, 1, 1, 60,
                                     4, 2, 0, 50];
                        suspendSeq = [3, 0, 2, 10];
                        resumeSeq = [3, 1, 2, 10];
                        powerOffSeq = [3, 0, 2, 10,
                                       1, 0, 2, 20];
                    }
                }
            }
        }
    }
    ...
}

1.3 将配置文件添加到板级配置:

将上面两个配置文件添加到板级配置入口文件hdf.hcs中
路径:./vendor/hihope/rk3568/hdf_config/khdf/hdf.hcs

#include "input/input_config.hcs"
#include "device_info/device_info.hcs"

2、驱动开发

2.1 开发驱动配置解析函数 【可选】####

路径:./drivers/hdf_core/framework/model/input/driver/input_config_parser.c
该函数主要将 hcs 文件中各字段含义进行解析,如果现有函数不满足hcs文件添加信息的解析,可以仿照现有函数添加新的解析方法

//公共驱动:HDF_TOUCH 私有配置信息 解析
int32_t ParseTouchBoardConfig(const struct DeviceResourceNode *node, TouchBoardCfg *config)
{
    ...
}

//gt911_5p5触摸屏私有驱动描述信息 解析
int32_t ParseTouchChipConfig(const struct DeviceResourceNode *node, TouchChipCfg *config)
{
    ...
}

2.2 驱动实现

对于touch 类型的设备,公共驱动框架已实现。

路径为:/drivers/hdf_core/framework/model/input/driver/touchscreen/hdf_touch.c

适配gt911_5p5触摸屏需要完成器件层驱动初始化、释放资源、注册驱动至HDF框架及触摸屏器件差异化接口适配

路径:/drivers/hdf_core/framework/model/input/driver/touchscreen/touch_gt911.c

具体实现函数如下:

2.2.1 注册到HDF框架

//驱动注册到HDF框架函数
struct HdfDriverEntry g_touchGoodixChipEntry = {
.moduleVersion = 1,
.moduleName = "HDF_TOUCH_GT911", // /驱动名称,该字段的值必须和驱动信息配置文件中moduleName的值一致
.Init = HdfGoodixChipInit,
.Release = HdfGoodixChipRelease,
};

HDF_INIT(g_touchGoodixChipEntry);

2.2.2 器件层驱动初始化

//器件层驱动初始化
static int32_t HdfGoodixChipInit(struct HdfDeviceObject *device)
{
    TouchChipCfg *chipCfg = NULL;
    ChipDevice *chipDev = NULL;

    HDF_LOGI("%s: enter", __func__);
    if (device == NULL) {
        return HDF_ERR_INVALID_PARAM;
    }
    /* 器件配置结构体内存申请、配置信息解析及挂载 */
    chipCfg = ChipConfigInstance(device);
    if (chipCfg == NULL) {
        return HDF_ERR_MALLOC_FAIL;
    }
    /* 器件实例化 */
    chipDev = ChipDeviceInstance();
    if (chipDev == NULL) {
        goto EXIT;
    }
    /* 器件信息挂载及器件私有操作挂载 */
    chipDev->chipCfg = chipCfg;
    chipDev->ops = &g_gt911ChipOps;
    chipDev->chipName = chipCfg->chipName;
    chipDev->vendorName = chipCfg->vendorName;
    device->priv = (void *)chipDev;
    /* 注册器件驱动至平台驱动 */
    if (RegisterTouchChipDevice(chipDev) != HDF_SUCCESS) {
        goto EXIT1;
    }
    HDF_LOGI("%s: exit succ, chipName = %s", __func__, chipCfg->chipName);
    return HDF_SUCCESS;

EXIT1:
    OsalMemFree(chipDev);
EXIT:
    FreeChipConfig(chipCfg);
    return HDF_FAILURE;
}

2.2.3 器件层驱动释放资源

static void HdfGoodixChipRelease(struct HdfDeviceObject *device)
{
    if (device == NULL || device->priv == NULL) {
        HDF_LOGE("%s: param is null", __func__);
        return;
    }
    HDF_LOGI("%s: goodix chip is release", __func__);
}

2.2.4 器件层驱动数据上报

正常流程需要实现HdfGoodixChipBind 函数用于,触摸屏和外部进行数据同行,但是公共驱动层HDF_TOUCH已经用HdfTouchDriverBind函数实现,器件不用重新实现,可以直接使用。
路径:/drivers/hdf_core/framework/model/input/driver/hdf_touch.c
static int32_t HdfTouchDriverBind(struct HdfDeviceObject *device)
{
if (device == NULL) {
HDF_LOGE("%s: param is null", func);
return HDF_ERR_INVALID_PARAM;
}

    static struct IDeviceIoService touchService = {
        .Dispatch = HdfTouchDispatch,
    };

    device->service = &touchService;
    return HDF_SUCCESS;

}

2.2.4 触摸屏器件差异化接口适配

触摸屏器件差异化接口适配
// gt911差异化接口适配
static struct TouchChipOps g_gt911ChipOps = {// 器件IC接口
.Init = ChipInit,// 初始化
.Detect = ChipDetect,// 器件检测
.Resume = ChipResume,// 唤醒
.Suspend = ChipSuspend,// 休眠
.DataHandle = ChipDataHandle,// 器件数据读取
.UpdateFirmware = UpdateFirmware,// 固件升级
.SetAbility = SetAbility,
};

static int32_t ChipInit(ChipDevice *device)
{
    return HDF_SUCCESS;
}

static int32_t ChipResume(ChipDevice *device)
{
    return HDF_SUCCESS;
}


static int32_t ChipSuspend(ChipDevice *device)
{
    return HDF_SUCCESS;
}

static int32_t ChipDetect(ChipDevice *device)
{
    ...
}

static int32_t ChipDataHandle(ChipDevice *device)
{
    ...
}

static int32_t UpdateFirmware(ChipDevice *device)
{
    ...
}

static void SetAbility(ChipDevice *device)
{
    ...
}

3、驱动编译配置

3.1、为内核增加对应的config参数

路径:.\drivers\hdf_core\adapter\khdf\linux\model\input\Kconfig
在Kconfig文件中添加 DRIVERS_HDF_TP_5P5_GT911 参数:

config DRIVERS_HDF_TP_5P5_GT911
bool "Enable HDF tp 5P5 GT911 driver"
default n
depends on DRIVERS_HDF_INPUT
help
  Answer Y to enable HDF TP 5P5 GT911 driver.

3.2、编译文件Makefile中添加驱动配置

路径:.\drivers\hdf_core\adapter\khdf\linux\model\input\Makefile
在Makefile文件中添加 DRIVERS_HDF_TP_5P5_GT911 编译配置:

obj-$(CONFIG_DRIVERS_HDF_TP_5P5_GT911) += \
          $(INPUT_ROOT_DIR)/touchscreen/touch_gt911.o

3.3、在rk3568内核参数中增加DRIVERS_HDF_TP_5P5_GT911参数

路径:.\kernel\linux/config/linux-5.10/arch/arm64/configs/rk3568_standard_defconfig
在rk3568_standard_defconfig文件中添加 DRIVERS_HDF_TP_5P5_GT911 = y 参数:

CONFIG_DRIVERS_HDF_INPUT=y
CONFIG_DRIVERS_HDF_TP_5P5_GT911=y

基于HDF框架编写用户态程序和驱动交互

当用户态应用和内核态驱动需要交互时,可以使用HDF框架的消息机制来实现.
消息机制的功能主要有以下两种:
1.用户态应用发送消息到驱动。
2.用户态应用接收驱动主动上报事件。

1.1、用户态应用发送消息到驱动

static int SendEvent(struct HdfIoService *serv, char *eventData)
{
    int ret = 0;
    struct HdfSBuf *data = HdfSBufObtainDefaultSize();
    if (data == NULL) {
        HDF_LOGE("fail to obtain sbuf data");
        return 1;
    }
    struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
    if (reply == NULL) {
        HDF_LOGE("fail to obtain sbuf reply");
        ret = HDF_DEV_ERR_NO_MEMORY;
        goto out;
    }
    if (!HdfSbufWriteString(data, eventData)) {
        HDF_LOGE("fail to write sbuf");
        ret = HDF_FAILURE;
        goto out;
    }
    ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("fail to send service call");
        goto out;
    }
    int replyData = 0;
    if (!HdfSbufReadInt32(reply, &replyData)) {
        HDF_LOGE("fail to get service call reply");
        ret = HDF_ERR_INVALID_OBJECT;
        goto out;
    }
    HDF_LOGE("Get reply is: %d", replyData);
out:
    HdfSBufRecycle(data);
    HdfSBufRecycle(reply);
    return ret;
}

1.2、用户态应用接收驱动主动上报事件

1.2.1、用户态编写驱动上报消息的处理函数

static int OnDevEventReceived(void *priv,  uint32_t id, struct HdfSBuf *data)
{
    OsalTimespec time;
    OsalGetTime(&time);
    HDF_LOGE("%s received event at %llu.%llu", (char *)priv, time.sec, time.usec);
    const char *string = HdfSbufReadString(data);
    if (string == NULL) {
        HDF_LOGE("fail to read string in event data");
        return -1;
    }
    HDF_LOGE("%s: dev event received: %d %s",  (char *)priv, id, string);
    return 0;
}

1.2.2、用户态编写驱动上报消息的处理函数

int RegisterListen()
{
    struct HdfIoService *serv = HdfIoServiceBind("sample_driver", 0);
    if (serv == NULL) {
        HDF_LOGE("fail to get service");
        return -1;
    }
    static struct HdfDevEventlistener listener = {
        .callBack = OnDevEventReceived,
        .priv ="Service0"
    };
    if (HdfDeviceRegisterEventListener(serv, &listener) != 0) {
        HDF_LOGE("fail to register event listener");
        return -1;
    }

    HdfDeviceUnregisterEventListener(serv, &listener);
    HdfIoServiceRecycle(serv);
    return 0;
}    

1.3、完整交互用例

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "hdf_log.h"
#include "hdf_sbuf.h"
#include "hdf_io_service_if.h"
#define HDF_LOG_TAG "sample_test"
#define SAMPLE_SERVICE_NAME "sample_service"
#define SAMPLE_WRITE_READ 123

int g_replyFlag = 0;
//驱动回调函数,通过接口注册到驱动用于驱动上报事件
static int OnDevEventReceived(void *priv,  uint32_t id, struct HdfSBuf *data)
{
    ......
}
static int SendEvent(struct HdfIoService *serv, char *eventData)
{
    ......
}
int main()
{
    char *sendData = "default event info";
    //绑定驱动服务
    struct HdfIoService *serv = HdfIoServiceBind(SAMPLE_SERVICE_NAME, 0);
    if (serv == NULL) {
        HDF_LOGE("fail to get service %s", SAMPLE_SERVICE_NAME);
        return HDF_FAILURE;
    }
    
    static struct HdfDevEventlistener listener = {
        .callBack = OnDevEventReceived,
        .priv ="Service0"
    };
    //注册驱动回调函数
    if (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS) {
            HDF_LOGE("fail to register event listener");
        return HDF_FAILURE;
    }
    //给驱动发送消息
    if (SendEvent(serv, sendData)) {
        HDF_LOGE("fail to send event");
        return HDF_FAILURE;
    }
    /* wait for event receive event finishing */
    while (g_replyFlag == 0) {
        sleep(1);
    }
    //注销驱动回调函数
    if (HdfDeviceUnregisterEventListener(serv, &listener)) {
        HDF_LOGE("fail to  unregister listener");
        return HDF_FAILURE;
    }
    //释放资源
    HdfIoServiceRecycle(serv);
    return HDF_SUCCESS;
}
Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐