概述
输入设备是用户与计算机系统进行人机交互的主要装置之一,是用户与计算机或者其他设备通信的桥梁。常见的输入设备有键盘、鼠标、游戏杆、触摸屏等。本文档将介绍如何使用 Hi3516DV300 开发板完成基于 HDF_Input 模型的触摸屏(Touch Screen)器件驱动开发,从而使开发者快速入门。
硬件资源简介
Touch 设备与主机通讯一般采用 I2C 总线完成数据的交互,为了提高触屏数据的实时性,触屏 IC 都会提供中断支持。当有触屏事件发生时,会触发主机中断管脚完成一次中断响应。中断处理函数中主机通过 I2C 总线读取触屏 IC 寄存器完成一次数据采集。
Hi3516DV300 开发板套件所提供的触摸屏器件 IC 为 GT911,该器件采用标准 I2C 与主机通信,通过 6pin 软排线与主板连接。6pin 分布以及实物连接图如下图所示:
Input模型简介
Input模型概览
Input 驱动模型核心部分由设备管理层、公共驱动层、器件驱动层组成。器件产生的数据借助平台数据通道能力从内核传递到用户态,驱动模型通过配置文件适配不同器件及硬件平台,提高开发者的器件驱动开发效率。如下部分为模型各部分的说明:
(1)input 设备管理:为各类输入设备驱动提供 input 设备的注册、注销接口,同时统一管理 input 设备列表;
(2)input 平台驱动:指各类 input 设备的公共抽象驱动(例如触摸屏的公共驱动),负责对板级硬件进行初始化、硬件中断处理、向 manager 注册 input 设备等;
(3)input 器件驱动:指各器件厂家的差异化驱动,通过适配平台驱动预留的差异化接口,实现器件驱动开发量最小化;
(4)input 数据通道:提供一套通用的数据上报通道,各类别的 input 设备驱动均可用此通道上报 input 事件;
(5)input 配置解析:负责对 input 设备的板级配置及器件私有配置进行解析及管理。
Input模型工作流程解析
为了让开发者更清晰的了解 Input 模型工作流程,本节将对 input 模型加载的关键流程代码进行说明。
本章节为 Input 模型工作流程说明,开发者无需进行开发。
私有配置信息解析
示例代码路径:
- ./drivers/framework/model/input/driver/input_config_parser.c
根据 OSAL 提供的配置解析函数,可以将 hcs 文件中各字段含义进行解析,具体请参考 input_config_parser.c 中各函数的实现。如果提供的模板不能满足需求,在 hcs 文件中添加相应信息后,需要根据添加的字段开发相应的解析函数。
- static int32_t ParseAttr(struct DeviceResourceIface *parser, const struct DeviceResourceNode *attrNode, BoardAttrCfg *attr)
- {
- int32_t ret;
- // 获取inputType字段信息,保存在BoardAttrCfg结构体中
- ret = parser->GetUint8(attrNode, "inputType", &attr->devType, 0);
- CHECK_PARSER_RET(ret, "GetUint8");
- ...
- return HDF_SUCCESS;
- }
管理驱动层初始化及注册驱动至HDF框架
示例代码路径:
- ./drivers/framework/model/input/driver/hdf_input_device_manager.c
- static int32_t HdfInputManagerInit(struct HdfDeviceObject *device)
- {
-
- g_inputManager = InputManagerInstance();
- ...
- }
- struct HdfDriverEntry g_hdfInputEntry = {
- .moduleVersion = 1,
- .moduleName = "HDF_INPUT_MANAGER",
- .Bind = HdfInputManagerBind,
- .Init = HdfInputManagerInit,
- .Release = HdfInputManagerRelease,
- };
- HDF_INIT(g_hdfInputEntry); //驱动注册入口
公共驱动层初始化及注册驱动至HDF框架
示例代码路径:
- ./drivers/framework/model/input/driver/hdf_touch.c
- static int32_t HdfTouchDriverProbe(struct HdfDeviceObject *device)
- {
- ...
-
- boardCfg = BoardConfigInstance(device);
- ...
-
- touchDriver = TouchDriverInstance();
- ...
-
- ret = TouchDriverInit(touchDriver, boardCfg);
- if (ret == HDF_SUCCESS) {
- ...
-
- AddTouchDriver(touchDriver);
- ...
- }
- ...
- }
- struct HdfDriverEntry g_hdfTouchEntry = {
- .moduleVersion = 1,
- .moduleName = "HDF_TOUCH",
- .Bind = HdfTouchDriverBind,
- .Init = HdfTouchDriverProbe,
- .Release = HdfTouchDriverRelease,
- };
-
- HDF_INIT(g_hdfTouchEntry); //驱动注册入口
器件驱动层初始化及注册驱动至HDF框架
具体请参考适配器件私有驱动器件层驱动初始化及注册驱动至 HDF 框架部分。
具体调用逻辑串联函数
Input 模型管理层驱动 init 函数初始化了设备管理链表,公共驱动层初始化函数完成了相关结构体的内存申请。器件驱动相关信息通过 RegisterChipDevice 函数对公共驱动层相关结构体进行信息填充,同时完成了相关硬件信息的初始化(如中断注册等),绑定设备与驱动组成 inputDev 通过 RegisterInputDevice 函数向驱动管理层进行注册,在 RegisterInputDevice 函数中主要实现了将 inputDev 向设备管理链表的添加等功能。如下所示为两个函数的实现部分:
函数具体实现代码位置
- ./drivers/framework/model/input/driver/hdf_touch.c
- int32_t RegisterChipDevice(ChipDevice *chipDev)
- {
- …
-
- DeviceBindDriver(chipDev);
- …
-
- ChipDriverInit(chipDev);
- …
-
- inputDev = InputDeviceInstance(chipDev);
- …
-
- RegisterInputDevice(inputDev);
- …
- }
函数具体实现代码位置
- ./drivers/framework/model/input/driver/hdf_input_device_manager.c
- int32_t RegisterInputDevice(InputDevice *inputDev)
- {
- …
-
- ret = AllocDeviceID(inputDev);
- …
-
- CreateDeviceNode(inputDev);
-
- AllocPackageBuffer(inputDev);
-
- AddInputDevice(inputDev);
- ···
- }
TouchScreen器件驱动开发
基于 Input 模型适配一款触摸屏 IC 需要完成的具体工作见下。
配置设备描述信息
驱动注册到 HDF 框架所需要的设备驱动描述信息,如驱动是否加载以及加载次序等。
配置文件路径:
- ./drivers/adapter/khdf/linux/hcs/device_info/device_info.hcs
device_info.hcs 中的信息主要提供给 HDF 框架使用,包含了 Input 模型各层驱动注册到 HDF 框架所必需的信息,开发者无特殊场景需求无需改动。各驱动层私有配置信息通过“deviceMatchAttr”字段与 input_config.hcs 中的“match_attr”相关内容进行匹配。
配置文件中与 input 模块相关的内容如下所示
- input :: host {
- hostName = "input_host";
- priority = 100;
- device_input_manager :: device { // Input管理层设备描述信息
- device0 :: deviceNode {
- policy = 2; // 向内核用户态均发布服务
- priority = 100; // input管理层驱动优先级默认为100
- preload = 0; // 加载该驱动
- permission = 0660; // 驱动创建设备节点权限
- moduleName = "HDF_INPUT_MANAGER"; // 与驱动入口moduleName匹配
- serviceName = "hdf_input_host"; // HDF框架生成的节点名
- deviceMatchAttr = ""; // manager目前不需要私有配置,因此为空
- }
- }
- device_hdf_touch :: device { // Input公共驱动层设备描述信息
- device0 :: deviceNode {
- policy = 2; // 向内核用户态均发布服务
- priority = 120; // input公共驱动优先级默认为120
- preload = 0; // 加载该驱动
- permission = 0660; // 驱动创建设备节点权限
- moduleName = "HDF_TOUCH"; // 与驱动入口的moduleName匹配
- serviceName = "hdf_input_event1"; // HDF框架生成的节点名
- deviceMatchAttr = "touch_device1"; // 与 “match_attr”字段一致
- }
- }
- device_touch_chip :: device { // Input器件驱动层信息
- device0 :: deviceNode {
- policy = 0; // 向内核用户态均不发布服务
- priority = 130; // input器件驱动优先级默认为130
- preload = 0; // 加载该驱动
- permission = 0660; // 驱动创建设备节点权限
- moduleName = "HDF_TOUCH_GT911"; // 与moduleName匹配
- serviceName = "hdf_touch_gt911_service"; // HDF框架节点名
- deviceMatchAttr = "zsj_gt911_5p5"; // 与“match_attr”字段一致
- }
- }
- }
该配置文件中需要重点关注的字段有:
“priority”决定驱动加载顺序;
“preload”决定驱动是否加载;
“moduleName ”需要与驱动注册入口处的“moduleName ”字段保持一致;
“serviceName ”HDF 框架依据该字段创建节点名;
“deviceMatchAttr ”需要与私有配置信息中的“match_attr”字段保持一致。
通过配置设备描述信息,使得 HDF 框架通过 moduleName 与注册至驱动入口的代码相匹配,保证了驱动的正常加载,通过 priority 字段保证了各驱动的加载顺序。
配置Touchscreen器件信息
器件私有信息包括上下电时序等,平台硬件信息包括器件连接主板的 GPIO 端口信息等。
配置文件路径:
- ./drivers/adapter/khdf/linux/hcs/input/input_config.hcs
input_config.hcs 中的信息由驱动代码进行读取解析,主要由公共驱动层的私有配置信息及器件驱动层的私有配置信息组成。文件中的配置包含板级硬件信息及器件私有配置信息,实际业务开发时,可根据具体需求增删及修改对应内容。
- pinConfig {
- rstGpio = 3; // 复位管脚连接主机芯片的3号管脚
- intGpio = 4; // 中断管脚连接主机芯片的4号管脚
- rstRegCfg = [0x112f0094, 0x400]; // 复位管脚配置信息
- intRegCfg = [0x112f0098, 0x400]; // 中断管脚配置信息
- }
- powerConfig {
-
- vccType = 2; // GPIO供电
- vccNum = 20; // gpio20
- vccValue = 1800; // 电压幅值为1800mV
- vciType = 1; // LDO供电
- vciNum = 12; // ldo12
- vciValue = 3300; // 电压幅值为3300mV
- }
- featureConfig {
- capacitanceTest = 0; // 容值测试
- gestureMode = 0; // 手势模式
- gloverMode = 0; // 手套模式
- coverMode = 0; // 皮套模式
- chargerMode = 0; // 充电模式
- knuckleMode = 0; // 指关节模式
- }
- }
- chipConfig { // 器件私有信息配置
- template touchChip { // 模板
- match_attr = "";
- chipName = "gt911"; // 触摸屏IC型号
- vendorName = "zsj"; // 供应商
- chipInfo = "AAAA11222";
-
- busType = 0; // 0代表I2C,1代表SPI
- deviceAddr = 0x5D; // 器件IC通信地址
-
- irqFlag = 2;
- maxSpeed = 400; // 最大通信速率为400Hz
- chipVersion = 0; // 触摸屏IC版本号
- powerSequence {
-
- powerOnSeq = [4, 0, 1, 0, // 中断管脚配置为输出,且进行拉低
- 3, 0, 1, 10, // 复位管脚配置为输出,且进行拉低,延时10ms
- 3, 1, 2, 60, // 复位管脚无操作,且进行拉高,延时60ms
- 4, 2, 0, 0]; // 中断管脚配置为输入
- suspendSeq = [3, 0, 2, 10]; // 复位管脚无操作,且进行拉低,延时10ms
- resumeSeq = [3, 1, 2, 10]; // 复位管脚无操作,且进行拉高,延时10ms
- powerOffSeq = [3, 0, 2, 10, // 复位管脚无操作,且进行拉低,延时10ms
- 1, 0, 2, 20]; // 电源正极管脚无操作,且进行拉低,延时20ms
- }
- }
- chip0 :: touchChip {
-
- match_attr = "zsj_gt911_5p5";
-
- chipInfo = "ZIDN45100";
-
- chipVersion = 0;
- }
- }
- }
- }
- }
- }
示例中“touchConfig”包含了“touch0”,"touch0"包含了“boardConfig”与“chipConfig”;“boardConfig”字段包含了 Hi3516DV300 板级硬件信息,“chipConfig”包含了触摸屏器件的私有信息,如果需要替换触摸屏器件,重新配置“chipConfig”对应的字段信息即可。同时产品可以配置多款触摸屏,示例中用“touch0”代表了套件中默认的触摸屏的硬件接口以及器件的配置信息,如产品需要配置副屏,可在与“touch0”并列的位置配置“touch1”的信息。
适配器件私有驱动
Input 模型对 Input 设备开发流程进行了抽象,开发者只需要适配器件驱动层,无需改动管理驱动层以及公共驱动层。
Input 模型由三层驱动组成,开发者适配一款全新触摸屏驱动只需要适配器件驱动层即可,重点实现差异化接口,本小节以代码示例的形式展示开发者需要重点完成的工作。
触摸屏器件差异化接口适配
示例代码路径
- ./drivers/framework/model/input/driver/touchscreen/touch_gt911.c
- static struct TouchChipOps g_gt911ChipOps = { // 器件IC接口
- .Init = ChipInit, // 初始化
- .Detect = ChipDetect, // 器件检测
- .Resume = ChipResume, // 唤醒
- .Suspend = ChipSuspend, // 休眠
- .DataHandle = ChipDataHandle, // 器件数据读取
- .UpdateFirmware = UpdateFirmware, // 固件升级
- };
-
- static int32_t ChipDataHandle(ChipDevice *device)
- {
- ...
-
- reg[0] = (GT_BUF_STATE_ADDR >> ONE_BYTE_OFFSET) & ONE_BYTE_MASK;
- reg[1] = GT_BUF_STATE_ADDR & ONE_BYTE_MASK;
- ret = InputI2cRead(i2cClient, reg, GT_ADDR_LEN, &touchStatus, 1);
- if (ret < 0 || touchStatus == GT_EVENT_INVALID) {
- return HDF_FAILURE;
- }
- ...
-
- reg[0] = (GT_X_LOW_BYTE_BASE >> ONE_BYTE_OFFSET) & ONE_BYTE_MASK;
- reg[1] = GT_X_LOW_BYTE_BASE & ONE_BYTE_MASK;
- pointNum = touchStatus & GT_FINGER_NUM_MASK;
- if (pointNum == 0 || pointNum > MAX_SUPPORT_POINT) {
- HDF_LOGE("%s: pointNum is invalid, %u", __func__, pointNum);
- (void)ChipCleanBuffer(i2cClient);
- OsalMutexUnlock(&device->driver->mutex);
- return HDF_FAILURE;
- }
- frame->realPointNum = pointNum;
- frame->definedEvent = TOUCH_DOWN;
- (void)InputI2cRead(i2cClient, reg, GT_ADDR_LEN, buf, GT_POINT_SIZE * pointNum);
-
- ParsePointData(device, frame, buf, pointNum);
- ...
- }
-
- static void ParsePointData(ChipDevice *device, FrameData *frame, uint8_t *buf, uint8_t pointNum)
- {
- ...
-
- for (i = 0; i < pointNum; i++) {
- frame->fingers[i].trackId = buf[GT_POINT_SIZE * i + GT_TRACK_ID];
- frame->fingers[i].y = (buf[GT_POINT_SIZE * i + GT_X_LOW] & ONE_BYTE_MASK) | ((buf[GT_POINT_SIZE * i + GT_X_HIGH] & ONE_BYTE_MASK) << ONE_BYTE_OFFSET);
- frame->fingers[i].x = (buf[GT_POINT_SIZE * i + GT_Y_LOW] & ONE_BYTE_MASK) | ((buf[GT_POINT_SIZE * i + GT_Y_HIGH] & ONE_BYTE_MASK) << ONE_BYTE_OFFSET);
-
- HDF_LOGD("%s: x = %d, y = %d", __func__, frame->fingers[i].x,
- frame->fingers[i].y);
- }
- }
器件层驱动初始化及注册驱动至HDF框架
示例代码路径
- ./drivers/framework/model/input/driver/touchscreen/touch_gt911.c
- static int32_t HdfGoodixChipInit(struct HdfDeviceObject *device)
- {
- ...
-
- chipCfg = ChipConfigInstance(device);
- ...
-
- chipDev = ChipDeviceInstance();
- ...
-
- chipDev->chipCfg = chipCfg;
- chipDev->ops = &g_gt911ChipOps;
- ...
-
- RegisterChipDevice(chipDev);
- ...
- }
- struct HdfDriverEntry g_touchGoodixChipEntry = {
- .moduleVersion = 1,
-
- .moduleName = "HDF_TOUCH_GT911",
- .Init = HdfGoodixChipInit, // 器件驱动初始化函数
- };
- HDF_INIT(g_touchGoodixChipEntry); // 注册器件驱动至HDF框架
器件私有驱动层主要实现了各器件厂商差异较大的部分,如器件休眠唤醒、数据解析以及固件升级等。
编译入口添加
编辑 Makefile 文件,添加本示例中的内容:
文件路径:
- ./drivers/adapter/khdf/linux/model/input/Makefile
添加内容如下:
- obj-$(CONFIG_DRIVERS_HDF_TP_5P5_GT911) += \
- $(INPUT_ROOT_DIR)/touchscreen/touch_gt911.o
其中 touch_gt911.o 为本示例中追加的内容
至此,基于 HDF 框架及 Input 模型的触摸屏驱动适配完成。
总结
本文梳理了 HDF_Input 模型工作流程,重点介绍了器件驱动适配,以 Hi3516dv300 开发板触屏为例进行了详细的代码说明,希望通过本文档您能初步掌握基于 HDF 框架的 Input 设备的开发步骤与流程。