OpenHarmony HDF Input驅(qū)動模型分析與使用
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
概述
輸入設備是用戶與計算機系統(tǒng)進行人機交互的主要裝置之一,是用戶與計算機或者其他設備通信的橋梁。常見的輸入設備有鍵盤、鼠標、游戲桿、觸摸屏等。本文檔將介紹如何使用 Hi3516DV300 開發(fā)板完成基于 HDF_Input 模型的觸摸屏(Touch Screen)器件驅(qū)動開發(fā),從而使開發(fā)者快速入門。
硬件資源簡介
Touch 設備與主機通訊一般采用 I2C 總線完成數(shù)據(jù)的交互,為了提高觸屏數(shù)據(jù)的實時性,觸屏 IC 都會提供中斷支持。當有觸屏事件發(fā)生時,會觸發(fā)主機中斷管腳完成一次中斷響應。中斷處理函數(shù)中主機通過 I2C 總線讀取觸屏 IC 寄存器完成一次數(shù)據(jù)采集。
Hi3516DV300 開發(fā)板套件所提供的觸摸屏器件 IC 為 GT911,該器件采用標準 I2C 與主機通信,通過 6pin 軟排線與主板連接。6pin 分布以及實物連接圖如下圖所示:

Input模型簡介
Input模型概覽

Input 驅(qū)動模型核心部分由設備管理層、公共驅(qū)動層、器件驅(qū)動層組成。器件產(chǎn)生的數(shù)據(jù)借助平臺數(shù)據(jù)通道能力從內(nèi)核傳遞到用戶態(tài),驅(qū)動模型通過配置文件適配不同器件及硬件平臺,提高開發(fā)者的器件驅(qū)動開發(fā)效率。如下部分為模型各部分的說明:
(1)input 設備管理:為各類輸入設備驅(qū)動提供 input 設備的注冊、注銷接口,同時統(tǒng)一管理 input 設備列表;
(2)input 平臺驅(qū)動:指各類 input 設備的公共抽象驅(qū)動(例如觸摸屏的公共驅(qū)動),負責對板級硬件進行初始化、硬件中斷處理、向 manager 注冊 input 設備等;
(3)input 器件驅(qū)動:指各器件廠家的差異化驅(qū)動,通過適配平臺驅(qū)動預留的差異化接口,實現(xiàn)器件驅(qū)動開發(fā)量最小化;
(4)input 數(shù)據(jù)通道:提供一套通用的數(shù)據(jù)上報通道,各類別的 input 設備驅(qū)動均可用此通道上報 input 事件;
(5)input 配置解析:負責對 input 設備的板級配置及器件私有配置進行解析及管理。
Input模型工作流程解析
為了讓開發(fā)者更清晰的了解 Input 模型工作流程,本節(jié)將對 input 模型加載的關(guān)鍵流程代碼進行說明。
本章節(jié)為 Input 模型工作流程說明,開發(fā)者無需進行開發(fā)。
私有配置信息解析
示例代碼路徑:
- ./drivers/framework/model/input/driver/input_config_parser.c
根據(jù) OSAL 提供的配置解析函數(shù),可以將 hcs 文件中各字段含義進行解析,具體請參考 input_config_parser.c 中各函數(shù)的實現(xiàn)。如果提供的模板不能滿足需求,在 hcs 文件中添加相應信息后,需要根據(jù)添加的字段開發(fā)相應的解析函數(shù)。
- static int32_t ParseAttr(struct DeviceResourceIface *parser, const struct DeviceResourceNode *attrNode, BoardAttrCfg *attr)
- {
- int32_t ret;
- // 獲取inputType字段信息,保存在BoardAttrCfg結(jié)構(gòu)體中
- ret = parser->GetUint8(attrNode, "inputType", &attr->devType, 0);
- CHECK_PARSER_RET(ret, "GetUint8");
- ...
- return HDF_SUCCESS;
- }
管理驅(qū)動層初始化及注冊驅(qū)動至HDF框架
示例代碼路徑:
- ./drivers/framework/model/input/driver/hdf_input_device_manager.c
- static int32_t HdfInputManagerInit(struct HdfDeviceObject *device)
- {
- /* 分配內(nèi)存給manager,manager中將存放所有input設備 */
- g_inputManager = InputManagerInstance();
- ...
- }
- struct HdfDriverEntry g_hdfInputEntry = {
- .moduleVersion = 1,
- .moduleName = "HDF_INPUT_MANAGER",
- .Bind = HdfInputManagerBind,
- .Init = HdfInputManagerInit,
- .Release = HdfInputManagerRelease,
- };
- HDF_INIT(g_hdfInputEntry); //驅(qū)動注冊入口
公共驅(qū)動層初始化及注冊驅(qū)動至HDF框架
示例代碼路徑:
- ./drivers/framework/model/input/driver/hdf_touch.c
- static int32_t HdfTouchDriverProbe(struct HdfDeviceObject *device)
- {
- ...
- /* 板級信息結(jié)構(gòu)體內(nèi)存申請及hcs配置信息解析 */
- boardCfg = BoardConfigInstance(device);
- ...
- /* 公共驅(qū)動結(jié)構(gòu)體內(nèi)存申請 */
- touchDriver = TouchDriverInstance();
- ...
- /* 依據(jù)解析出的板級信息進行公共資源初始化,如IIC初始化 */
- ret = TouchDriverInit(touchDriver, boardCfg);
- if (ret == HDF_SUCCESS) {
- ...
- /* 添加驅(qū)動至公共驅(qū)動層驅(qū)動管理鏈表,當設備與驅(qū)動進行綁定時使用該鏈表進行查詢 */
- AddTouchDriver(touchDriver);
- ...
- }
- ...
- }
- struct HdfDriverEntry g_hdfTouchEntry = {
- .moduleVersion = 1,
- .moduleName = "HDF_TOUCH",
- .Bind = HdfTouchDriverBind,
- .Init = HdfTouchDriverProbe,
- .Release = HdfTouchDriverRelease,
- };
- HDF_INIT(g_hdfTouchEntry); //驅(qū)動注冊入口
器件驅(qū)動層初始化及注冊驅(qū)動至HDF框架
具體請參考適配器件私有驅(qū)動器件層驅(qū)動初始化及注冊驅(qū)動至 HDF 框架部分。
具體調(diào)用邏輯串聯(lián)函數(shù)
Input 模型管理層驅(qū)動 init 函數(shù)初始化了設備管理鏈表,公共驅(qū)動層初始化函數(shù)完成了相關(guān)結(jié)構(gòu)體的內(nèi)存申請。器件驅(qū)動相關(guān)信息通過 RegisterChipDevice 函數(shù)對公共驅(qū)動層相關(guān)結(jié)構(gòu)體進行信息填充,同時完成了相關(guān)硬件信息的初始化(如中斷注冊等),綁定設備與驅(qū)動組成 inputDev 通過 RegisterInputDevice 函數(shù)向驅(qū)動管理層進行注冊,在 RegisterInputDevice 函數(shù)中主要實現(xiàn)了將 inputDev 向設備管理鏈表的添加等功能。如下所示為兩個函數(shù)的實現(xiàn)部分:
函數(shù)具體實現(xiàn)代碼位置
- ./drivers/framework/model/input/driver/hdf_touch.c
- int32_t RegisterChipDevice(ChipDevice *chipDev)
- {
- …
- /* 綁定設備與驅(qū)動,從而通過InputDeviceInstance函數(shù)創(chuàng)建inputDev */
- DeviceBindDriver(chipDev);
- …
- /* 主要包含器件中斷注冊及中斷處理函數(shù),處理函數(shù)中有數(shù)據(jù)上報用戶態(tài)的數(shù)據(jù)通道 */
- ChipDriverInit(chipDev);
- …
- /* 申請內(nèi)存實例化InputDev */
- inputDev = InputDeviceInstance(chipDev);
- …
- /* 將InputDev設備注冊至input驅(qū)動管理層 */
- RegisterInputDevice(inputDev);
- …
- }
函數(shù)具體實現(xiàn)代碼位置
- ./drivers/framework/model/input/driver/hdf_input_device_manager.c
- int32_t RegisterInputDevice(InputDevice *inputDev)
- {
- …
- /* 申請ID,該ID對于不同input設備唯一 */
- ret = AllocDeviceID(inputDev);
- …
- /* 該函數(shù)包含了對hid類設備的特殊處理,對于觸摸屏驅(qū)動,該函數(shù)無實質(zhì)操作; */
- CreateDeviceNode(inputDev);
- /* 內(nèi)核態(tài)數(shù)據(jù)傳送至用戶態(tài)需使用IOService能力,需要申請buffer */
- AllocPackageBuffer(inputDev);
- /* 將input設備添加進設備全局管理鏈表 */
- AddInputDevice(inputDev);
- ···
- }
TouchScreen器件驅(qū)動開發(fā)
基于 Input 模型適配一款觸摸屏 IC 需要完成的具體工作見下。
配置設備描述信息
驅(qū)動注冊到 HDF 框架所需要的設備驅(qū)動描述信息,如驅(qū)動是否加載以及加載次序等。
配置文件路徑:
- ./drivers/adapter/khdf/linux/hcs/device_info/device_info.hcs
device_info.hcs 中的信息主要提供給 HDF 框架使用,包含了 Input 模型各層驅(qū)動注冊到 HDF 框架所必需的信息,開發(fā)者無特殊場景需求無需改動。各驅(qū)動層私有配置信息通過“deviceMatchAttr”字段與 input_config.hcs 中的“match_attr”相關(guān)內(nèi)容進行匹配。
配置文件中與 input 模塊相關(guān)的內(nèi)容如下所示
- input :: host {
- hostName = "input_host";
- priority = 100;
- device_input_manager :: device { // Input管理層設備描述信息
- device0 :: deviceNode {
- policy = 2; // 向內(nèi)核用戶態(tài)均發(fā)布服務
- priority = 100; // input管理層驅(qū)動優(yōu)先級默認為100
- preload = 0; // 加載該驅(qū)動
- permission = 0660; // 驅(qū)動創(chuàng)建設備節(jié)點權(quán)限
- moduleName = "HDF_INPUT_MANAGER"; // 與驅(qū)動入口moduleName匹配
- serviceName = "hdf_input_host"; // HDF框架生成的節(jié)點名
- deviceMatchAttr = ""; // manager目前不需要私有配置,因此為空
- }
- }
- device_hdf_touch :: device { // Input公共驅(qū)動層設備描述信息
- device0 :: deviceNode {
- policy = 2; // 向內(nèi)核用戶態(tài)均發(fā)布服務
- priority = 120; // input公共驅(qū)動優(yōu)先級默認為120
- preload = 0; // 加載該驅(qū)動
- permission = 0660; // 驅(qū)動創(chuàng)建設備節(jié)點權(quán)限
- moduleName = "HDF_TOUCH"; // 與驅(qū)動入口的moduleName匹配
- serviceName = "hdf_input_event1"; // HDF框架生成的節(jié)點名
- deviceMatchAttr = "touch_device1"; // 與 “match_attr”字段一致
- }
- }
- device_touch_chip :: device { // Input器件驅(qū)動層信息
- device0 :: deviceNode {
- policy = 0; // 向內(nèi)核用戶態(tài)均不發(fā)布服務
- priority = 130; // input器件驅(qū)動優(yōu)先級默認為130
- preload = 0; // 加載該驅(qū)動
- permission = 0660; // 驅(qū)動創(chuàng)建設備節(jié)點權(quán)限
- moduleName = "HDF_TOUCH_GT911"; // 與moduleName匹配
- serviceName = "hdf_touch_gt911_service"; // HDF框架節(jié)點名
- deviceMatchAttr = "zsj_gt911_5p5"; // 與“match_attr”字段一致
- }
- }
- }
該配置文件中需要重點關(guān)注的字段有:
“priority”決定驅(qū)動加載順序;
“preload”決定驅(qū)動是否加載;
“moduleName ”需要與驅(qū)動注冊入口處的“moduleName ”字段保持一致;
“serviceName ”HDF 框架依據(jù)該字段創(chuàng)建節(jié)點名;
“deviceMatchAttr ”需要與私有配置信息中的“match_attr”字段保持一致。
通過配置設備描述信息,使得 HDF 框架通過 moduleName 與注冊至驅(qū)動入口的代碼相匹配,保證了驅(qū)動的正常加載,通過 priority 字段保證了各驅(qū)動的加載順序。
配置Touchscreen器件信息
器件私有信息包括上下電時序等,平臺硬件信息包括器件連接主板的 GPIO 端口信息等。
配置文件路徑:
- ./drivers/adapter/khdf/linux/hcs/input/input_config.hcs
input_config.hcs 中的信息由驅(qū)動代碼進行讀取解析,主要由公共驅(qū)動層的私有配置信息及器件驅(qū)動層的私有配置信息組成。文件中的配置包含板級硬件信息及器件私有配置信息,實際業(yè)務開發(fā)時,可根據(jù)具體需求增刪及修改對應內(nèi)容。
- pinConfig {
- rstGpio = 3; // 復位管腳連接主機芯片的3號管腳
- intGpio = 4; // 中斷管腳連接主機芯片的4號管腳
- rstRegCfg = [0x112f0094, 0x400]; // 復位管腳配置信息
- intRegCfg = [0x112f0098, 0x400]; // 中斷管腳配置信息
- }
- powerConfig {
- /* 0:unused 1:ldo 2:gpio 3:pmic */
- 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; // 指關(guān)節(jié)模式
- }
- }
- chipConfig { // 器件私有信息配置
- template touchChip { // 模板
- match_attr = "";
- chipName = "gt911"; // 觸摸屏IC型號
- vendorName = "zsj"; // 供應商
- chipInfo = "AAAA11222";
- /*1~4字符代表產(chǎn)品名,5~6字符代表IC型號,7~9字符代表模型型號*/
- busType = 0; // 0代表I2C,1代表SPI
- deviceAddr = 0x5D; // 器件IC通信地址
- /* 1代表上升沿觸發(fā),2代表下降沿觸發(fā),4代表高電平觸發(fā),8代表低電平觸發(fā)*/
- irqFlag = 2;
- maxSpeed = 400; // 最大通信速率為400Hz
- chipVersion = 0; // 觸摸屏IC版本號
- powerSequence {
- /* 上電時序的配置含義說明: [類型, 狀態(tài), 方向 , 延時]
- <type> 0代表空,1代表vcc電源1.8V,2代表VCI電源3.3V,
- 3代表復位管腳,4代表中斷管腳
- <status> 0代表下電或拉低,1代表上電或拉高,2代表無操作
- <dir> 0代表輸入方向,1代表輸出方向,2代表無操作
- <delay> 代表延時多少毫秒, 例如20代表延時20ms
- */
- 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”字段保持一致 */
- match_attr = "zsj_gt911_5p5";
- /* 產(chǎn)品名+模組編號+芯片編號的組合信息 用于給用戶態(tài)區(qū)分當前器件 */
- chipInfo = "ZIDN45100";
- /* IC型號的版本 */
- chipVersion = 0;
- }
- }
- }
- }
- }
- }
示例中“touchConfig”包含了“touch0”,"touch0"包含了“boardConfig”與“chipConfig”;“boardConfig”字段包含了 Hi3516DV300 板級硬件信息,“chipConfig”包含了觸摸屏器件的私有信息,如果需要替換觸摸屏器件,重新配置“chipConfig”對應的字段信息即可。同時產(chǎn)品可以配置多款觸摸屏,示例中用“touch0”代表了套件中默認的觸摸屏的硬件接口以及器件的配置信息,如產(chǎn)品需要配置副屏,可在與“touch0”并列的位置配置“touch1”的信息。
適配器件私有驅(qū)動
Input 模型對 Input 設備開發(fā)流程進行了抽象,開發(fā)者只需要適配器件驅(qū)動層,無需改動管理驅(qū)動層以及公共驅(qū)動層。
Input 模型由三層驅(qū)動組成,開發(fā)者適配一款全新觸摸屏驅(qū)動只需要適配器件驅(qū)動層即可,重點實現(xiàn)差異化接口,本小節(jié)以代碼示例的形式展示開發(fā)者需要重點完成的工作。
觸摸屏器件差異化接口適配
示例代碼路徑
- ./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, // 器件數(shù)據(jù)讀取
- .UpdateFirmware = UpdateFirmware, // 固件升級
- };
- /* 不同觸摸屏廠家使用的IC不一樣,對應的寄存器操作也不一樣,因此器件驅(qū)動層代碼重點適配差異化接口部分,如下示例代碼展示了GT911的數(shù)據(jù)解析*/
- static int32_t ChipDataHandle(ChipDevice *device)
- {
- ...
- /* GT911獲取坐標之前需先讀取狀態(tài)寄存器 */
- 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;
- }
- ...
- /* 根據(jù)狀態(tài)寄存器的值讀取數(shù)據(jù)寄存器數(shù)據(jù) */
- 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);
- /* 對獲取的數(shù)據(jù)進行解析 */
- ParsePointData(device, frame, buf, pointNum);
- ...
- }
- static void ParsePointData(ChipDevice *device, FrameData *frame, uint8_t *buf, uint8_t pointNum)
- {
- ...
- /* 每個坐標值由兩個字節(jié)組成,對獲取的單字節(jié)數(shù)據(jù)進行拼接得到最終的坐標值 */
- 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);
- }
- }
器件層驅(qū)動初始化及注冊驅(qū)動至HDF框架
示例代碼路徑
- ./drivers/framework/model/input/driver/touchscreen/touch_gt911.c
- static int32_t HdfGoodixChipInit(struct HdfDeviceObject *device)
- {
- ...
- /* 器件配置結(jié)構(gòu)體內(nèi)存申請、配置信息解析及掛載 */
- chipCfg = ChipConfigInstance(device);
- ...
- /* 器件實例化 */
- chipDev = ChipDeviceInstance();
- ...
- /* 器件信息掛載及器件私有操作掛載 */
- chipDev->chipCfg = chipCfg;
- chipDev->ops = &g_gt911ChipOps;
- ...
- /* 注冊器件驅(qū)動至平臺驅(qū)動 */
- RegisterChipDevice(chipDev);
- ...
- }
- struct HdfDriverEntry g_touchGoodixChipEntry = {
- .moduleVersion = 1,
- /* 該moduleName與device_info.hcs文件中器件驅(qū)動層的moduleName信息相匹配*/
- .moduleName = "HDF_TOUCH_GT911",
- .Init = HdfGoodixChipInit, // 器件驅(qū)動初始化函數(shù)
- };
- HDF_INIT(g_touchGoodixChipEntry); // 注冊器件驅(qū)動至HDF框架
器件私有驅(qū)動層主要實現(xiàn)了各器件廠商差異較大的部分,如器件休眠喚醒、數(shù)據(jù)解析以及固件升級等。
編譯入口添加
編輯 Makefile 文件,添加本示例中的內(nèi)容:
文件路徑:
- ./drivers/adapter/khdf/linux/model/input/Makefile
添加內(nèi)容如下:
- obj-$(CONFIG_DRIVERS_HDF_TP_5P5_GT911) += \
- $(INPUT_ROOT_DIR)/touchscreen/touch_gt911.o
其中 touch_gt911.o 為本示例中追加的內(nèi)容
至此,基于 HDF 框架及 Input 模型的觸摸屏驅(qū)動適配完成。
總結(jié)
本文梳理了 HDF_Input 模型工作流程,重點介紹了器件驅(qū)動適配,以 Hi3516dv300 開發(fā)板觸屏為例進行了詳細的代碼說明,希望通過本文檔您能初步掌握基于 HDF 框架的 Input 設備的開發(fā)步驟與流程。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)