HDF 和 HCS 在开源鸿蒙系统里干什么?—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
设备树是 Linux 认硬件的办法。今天看开源鸿蒙自己那套驱动框架触摸、音频、显示很大一部分走这边和设备树各进一张镜像。你要是从 Linux 那边过来第一次碰开源鸿蒙的驱动最容易犯的错不是写不出 probe而是改完配置板上完全没反应。设备树你改了HCS 你也改了全量编译 exit 0镜像刷进去触摸还是按 800×1280 走分辨率明明已经写成 1024×600。这不是编译器跟你作对。是开源鸿蒙把「硬件被谁认出来」拆成了两套世界一套仍是 Linux 的设备树一套叫HDF。两套各有一份配置、各进一张镜像、各有一份缓存。你改的那份很可能根本没被编进去或者编进去了你刷的是另一张分区。设备树还要继续用。HDF 和 DTS 是一对缺一边后面触摸、音频、显示会改错地方。官方模型先放在这对着看比空背术语快1. 先把场景说清楚谁在找谁传统 Linux 嵌入式里驱动是.c加 Kconfig 加 DTS用户态open(/dev/input/event0)。应用认的是节点路径。节点在应用就能干活。开源鸿蒙标准系统底下确实还是 Linux 5.10/dev也还在。可桌面、Ability、ArkTS Kit 并不靠你随手 open 一个字符设备过日子。图形要找 composer输入要找多模输入音频要找 ADM。这些子系统认的是HDI——Hardware Device Interface一套稳定的接口不认芯片型号也不认你给节点起的名字。HDI 下面那一层就是HDFHardware Driver Foundation。它干三件很具体的事第一按名单加载驱动。名单不在 DTS 里在 HCS 里。框架拿着device_info.hcs逐个对moduleName对上了就调你的Bind和Init。第二把驱动包装成服务。每个 DeviceNode 可以对外发布一个服务。内核里的别的驱动能 GetService用户态的 host 进程也能 Bind。发不发布、谁看得见由一个叫policy的整数决定。这个整数写错是用户态「驱动明明起来了却拿不到」的第一号元凶。第三给用户态和内核态一条统一的消息路。不必每人自己 invent 一套 ioctl 编号。用户态Dispatch内核态HdfDeviceSendEvent往回推。它的宣传语是「一次编写、多内核部署」。LiteOS 上能跑的 HDF 驱动理想状态下搬到 Linux 上只换 OSAL。RK3568 这块板跑的是标准系统内核是 Linux所以你实际看到的是Linux 原生驱动和 HDF 并存。并存不是文档里的小字是每天会咬人的结构同一颗 GT911内核的 goodix 驱动和 HDF 的触摸模型都想占 I2C2 的0x5d。谁先 probe 谁赢另一个开始 NACK。同一条 I2C 怎么拆下文单独讲。官方概述在这写得比我规范但例子偏通用芯片驱动子系统基础2. 对着官方图把五块积木认回家图从上往下我按你改代码时真正会进的目录说。HDI是门面。drivers/interface/里一堆 IDL编译出的是服务端骨架和客户端桩。应用同学调ohos.multimodalInput、ohos.multimedia.audio最后会落到这里。你要是南向很少直接改 IDL但要知道HDI 稳定底下换芯片不能把接口改没。HDF 框架在drivers/hdf_core/framework/。加载、Host、服务管理、消息、配置解析都在这。你写驱动时实现的Bind/Init/Release就是交给它调的。它不管你的芯片发哪几个寄存器它管「这个模块叫什么名字、什么时候加载、服务发给谁」。OSAL是操作系统抽象。驱动里不要直接kmalloc、不要直接mutex_lock用OsalMemAlloc、OsalMutex。为什么因为同一份驱动还想在 LiteOS 上编过。RK3568 上 OSAL 的实现落到 Linux 内核 API对你来说就是多包一层。bring-up 阶段有人图省事直接调内核 API短期能跑后面一开 CFI 或者一换编译选项就炸。能走 OSAL 就走 OSAL。平台驱动是腿。GPIO、I2C、SPI、UART、ADC、PWM、RTC、MMC这些不叫「外设模型」叫「把板子上的总线和脚交给别人用」。外设模型通过框架 API 向 I2C 管理器要一次 transfer不必自己填i2c_msg。HCS 里HDF_PLATFORM_I2C_MANAGER那种条目就是在登记这些腿。外设模型是身子。Input、Display、Audio、Camera、Sensor。模型规定「触摸要上报坐标」「显示要给 composer 提供层」「音频要走 ADM 的 render/capture」。芯片差异尽量关在模型底下的芯片驱动里。上层 Kit 认模型不认 GT911 还是 FT5406。仓库可以记这张地图改的时候少逛错目录drivers/hdf_core/framework/ 框架、平台、模型、hc-gen drivers/hdf_core/adapter/khdf/linux/ 接到 Linux 内核的那一层 drivers/peripheral/ Display / Input / Audio / Camera 的 HAL drivers/interface/ HDI 接口定义 vendor/rk/rk3568_evb/hdf_config/khdf/ 编进内核的 HCS vendor/rk/rk3568_evb/hdf_config/uhdf/ 打进 vendor.img 的 HCS平台驱动管「脚和总线」外设模型管「这类器件对系统长什么样」。PWM 风扇只是一个占空比走平台 PWM 就够电容触摸要进桌面滑动必须走 Input 模型。别把超声波硬塞进 Sensor 模型——你没有标准 Kit 要接它外设测试应用 open 一个/dev/hcsr04更直接。内核原生、HDF 平台、HDF 外设三条路怎么选下一篇写。3. 一次触摸从手指走到 ArkTS空讲分层容易飘。走一遍触摸。手指点到屏上GT911 拉低 INT。这根脚在 HDF 的input_config.hcs里写成intGpio 23GPIO0_C7。khdf 里的触摸芯片驱动收到中断按 I2C2 去读坐标寄存器。I2C 交易不是芯片驱动自己开的它找HDF_PLATFORM_I2C_MANAGER要一次 transfer。管理器再落到 Linux 的i2c_adapter。坐标读回来芯片驱动交给 Input 模型。模型按solutionX 1024、solutionY 600做缩放——注意这里的分辨率是 HCS 里的不是 DTS 里的。你只改 DTS 的touchscreen-size-xHDF 接管时根本不看那两个属性。这就是很多人「DTS 改了触摸还是歪」的原因。模型上报给多模输入再给窗口最后 ArkTS 的点击回调到了。整条链上应用没 open 过/dev/input/event0。你用cat /proc/bus/input/devices仍可能看到一个 input 设备那是模型在内核侧登记的给兼容路径用不是应用的主路。音频更明显HDF ADM 起来之后板上没有/dev/snd也没有/proc/asound。你按 ALSA 的习惯去找 card0会以为声卡没驱动。其实四个节点在/dev/hdf_audio_*。走错框架诊断全废。显示是第三条典型 HDF 路。composer 在用户态panel 入口在内核中间还夹着 DRM/KMS。这里只要记住显示不完全是「写好 DTS 就出桌面」HDF 的 panel 配置找不到 compatible 时/dev/dri/card0都可能不建。4. khdf 和 uhdf两套配置进两张镜像这是最值钱的分工也是「我改了怎么没反应」的答案所在。HCS 源码在板级vendor/rk/rk3568_evb/hdf_config/下分成两棵树hdf_config/ ├── khdf/ │ ├── hdf.hcs # 顶层几乎全是 #include │ ├── device_info/ │ ├── input/ │ ├── lcd/ │ ├── audio/ │ └── platform/ └── uhdf/ ├── device_info.hcs └── 用户态 host 用的配置khdf 跟内核一起跑。触摸芯片驱动、部分音频、部分显示 panel 入口读的是这套。hc-gen 把它编成二进制再转成一个 C 数组链进内核 Image。所以改 khdf 改boot_linux.img显示相关还要改 resource 分区里的 dtb。uhdf 给用户态 host 用。composer、部分平台适配跑在进程里读 vendor 分区里的hdf_default.hcb。改 uhdf 改vendor.img。两套可以长得很像字段名都叫device_info缓存却互不相认。你改了 khdf 的input_config.hcs图省事只刷了 vendor板上触摸分辨率纹丝不动。日志也正常因为内核里跑的还是上一份 hex。管道画清楚HCS 源码你改的是这些 .hcs │ ┌────────────┴────────────┐ │ │ khdf/hdf.hcs uhdf/*.hcs │ │ hc-gen hc-gen │ │ hdf_hcs.hcb hdf_default.hcb │ │ hdf_hcs_hex.c 拷进 vendor.img unsigned char 数组 /vendor/etc/hdfconfig/ │ 链进 vmlinux / Image │ boot_linux.imgkhdf 的 make 规则精简后是这样。注意看依赖HCB_FLAGS : -b -i -a HCS_OBJ : hdf_hcs_hex.o $(CONFIG_GEN_HEX_SRC): $(LOCAL_HCS_ROOT)/%_hcs_hex.c: $(HCS_DIR)/%.hcs | $(HC_GEN) $(Q)echo gen hdf built-in config $(Q)$(HC_GEN) $(HCB_FLAGS) -o $(subst _hex.c,,$()) $ obj-$(CONFIG_DRIVERS_HDF) $(HCS_OBJ)依赖项写的是顶层hdf.hcs。#include进去的device_info.hcs、input_config.hcs、lcd_config.hcs不在依赖里。make 判断「要不要重生成 hex」只看hdf.hcs的时间戳。你改子文件、顶层一行没动make 认为没变继续拿上次的hdf_hcs_hex.c去 cc。链接成功exit 0镜像是旧配置。这不是理论。触摸从 FT5406 切到 GT911 的时候HCS 改了重建烧录板上仍按 FT5406 的复位脚去拉。hcb 停留在两天前。从那以后板级build_kernel.sh编内核前必须无条件删 hcb不能指望 make 变聪明。# 编内核前khdf 的 hcb / hex 缓存无条件清 rm -f vendor/rk/rk3568_evb/hdf_config/khdf/hdf.hcb rm -f vendor/rk/rk3568_evb/hdf_config/khdf/hdf_hcs.hcb find out/kernel -name hdf_hcs_hex.c -o -name hdf_hcs_hex.o -o -name hdf.hcb \ | xargs --no-run-if-empty rm -fuhdf 是另一对文件别和上面删混rm -f out/rk3568_evb/gen/vendor/rk/rk3568_evb/hdf_config/uhdf/hdf_default.hcb rm -f out/rk3568_evb/packages/phone/vendor/etc/hdfconfig/hdf_default.hcb rm -f out/rk3568_evb/packages/phone/images/vendor.img编完不要只看 exit 码。Image 是二进制但 HCS 里的字符串还在里面grep -a能直接搜# 主机新字符串能搜到才算 khdf 真进了内核 grep -a HDF_TOUCH_GT911 out/kernel/OBJ/linux-5.10/arch/arm64/boot/Image | head grep -a main_touch out/kernel/OBJ/linux-5.10/arch/arm64/boot/Image | head # 把 solutionX 从 800 改成 1024 之后旧的 800 不该再作为触摸配置出现搜不到就别刷。刷了也是上一份。ninja 对 board 目录经常不重跑和这个坑叠在一起能让人连续两天以为「HCS 语法错了」。最小重建那篇会拆 checkpoint。5. HCS 本身它不是 C也不是 DTSHCS 是 HDF 自己的配置语言key-value 树。设计目的很明确把配置从驱动代码里拆出去。驱动里不要写死总线号、不要写死复位脚启动时用match_attr把对应节点找回来。换板换脚理论上只改 HCS。顶层hdf.hcs几乎全是 include真正内容在子文件#include device_info/device_info.hcs #include platform/adc_config_linux.hcs #include platform/pwm_config.hcs #include platform/rk3568_uart_config.hcs #include input/input_config.hcs #include camera/camera_config.hcs #include audio/audio_config.hcs #include lcd/lcd_config.hcs root { module rockchip,rk3568_chip; }几个语法点写的时候少被 hc-gen 骂属性必须属于一个节点必须以分号结束。节点用花括号后面没有分号。这和 C 结构体初始化相反手滑加个分号报错信息不一定指到那一行。每个文件一个root。root里必须有module给这份配置贴标签。template定义模板foo :: templateName { ... }继承后再覆盖字段。device_info.hcs里大段都是这套写法看起来像面向对象其实就是少抄几遍priority 100。match_attr是全局唯一字符串。驱动启动时拿这个字符串去配置树里找节点。device_info.hcs的deviceMatchAttr必须和配置节点的match_attr逐字符相同。差一个下划线驱动 Init 时拿到空配置表现为「脚是随机值」或者直接 Init 失败。include拼树。delete只能删 include 进来的节点不能删本文件自己写的。触摸这块分辨率、总线、复位脚都在input_config.hcs不在 DTSroot { input_config { touchConfig { touch0 { boardConfig { match_attr touch_device1; inputAttr { inputType 0; /* 0 touch */ solutionX 1024; solutionY 600; devName main_touch; } busConfig { busType 0; /* 0 i2c */ busNum 2; /* I2C2 */ } pinConfig { rstGpio 88; /* GPIO2_D0LVDS 配 GT911 */ intGpio 23; /* GPIO0_C7 */ } } } } } }solutionX/Y必须跟屏的逻辑分辨率一致。LVDS 和 RGB 都是 1024×600 横屏MIPI 那块常见 800×1280 竖屏这两个数字要一起改只改屏不改触摸划起来整块玻璃是歪的。HDF 触摸的几何在 HCS不在 DTS。背光若走 HDF PWMlcd_config.hcs指 PWM 号。RK3568 这块底板常见 PWM4root { backlightConfig { pwmBacklightConfig { match_attr pwm_bl_dev; pwmDevNum 4; pwmMaxPeriod 25000; backlightDevName hdf_pwm; minBrightness 0; defBrightness 127; maxBrightness 255; } } }PWM 号写错表现为屏有时序、有 HDMI 那样的影像就是背光不亮。你拿示波器去量 PWM4 没波形量到别的 PWM 上才有就是这份配置指错了。别先怀疑 panel 驱动。HCS 和 DTS 的分工可以记一句人话DTS 告诉 Linux 内核「这块硅有哪些控制器、哪些脚、哪些时钟」。HCS 告诉 HDF「哪个模型用哪条总线、哪根脚、什么分辨率、服务叫什么」。两份都要而且不要让它们抢同一颗从设备。6. device_info.hcs户口本不是注释框架加载谁、以什么策略发布服务全看这份文件。它不负责时序不负责坐标只负责「人」。模板通常长这样root { device_info { match_attr hdf_manager; template host { hostName ; priority 100; template device { template deviceNode { policy 0; priority 100; preload 0; permission 0664; moduleName ; serviceName ; deviceMatchAttr ; } } } } }字段一个一个钉死。写错的代价不是编译失败是运行时静默缺席。hostName是容器名。一类驱动放一个 Host。平台放platform_host输入放input_host。划分 Host 的原则是耦合两个驱动互相 GetService放一起更省事完全无关就分开一个挂了别把另一类拖死。用户态 Host 往往是一个进程内核态 Host 是框架里的一组对象。priority取值 0 到 200越小越先加载。先比 Host再比 Host 里的设备。平台 Host 填 50、输入 Host 填 100是因为触摸 Init 时要向 I2C 管理器发消息管理器必须已经在。你要是图个整洁把 platform_host 调到 200触摸会在 I2C 还没起来时失败日志只说 transfer failed不说「你把加载顺序写反了」。policy下一节单独讲。这里先记用户态要拿服务必须是 2。preload0开机加载1快启第二阶段2第一次 GetService 再加载。显示、触摸、音频用0。不要把触摸设成2还指望开机就能划解锁。permission是设备节点权限。开发期 0666 能少踩 SELinux 和 hap 权限的坑交付再收紧。现在 permissive 也别太得意hap 自己的 ACL 还能把你挡在节点外面那是签名和权限另说。moduleName必须和驱动注册的名字逐字符相同。驱动里是HDF_TOUCH_GT911户口本写成HDF_TOUCH_gt911框架加载时找不到设备缺席。dmesg 里经常只有一句很淡的load driver failed。先对这份户口本再怀疑芯片虚焊。serviceName是 GetService / Bind 用的名字。用户态写hdf_input_host0户口本写成hdf_input_hostBind 返回空指针。两边对着抄不要凭记忆。deviceMatchAttr去配置树里找私有配置。必须对上match_attr。一段能用的平台 触摸登记如下。结构来自板级名字按教程代号rk3568_evbplatform :: host { hostName platform_host; priority 50; device_gpio :: device { device0 :: deviceNode { policy 2; priority 10; permission 0644; moduleName HDF_PLATFORM_GPIO_MANAGER; serviceName HDF_PLATFORM_GPIO_MANAGER; } device1 :: deviceNode { policy 0; priority 10; moduleName linux_gpio_adapter; deviceMatchAttr linux_gpio_adapter; } } device_i2c :: device { device0 :: deviceNode { policy 2; priority 50; moduleName HDF_PLATFORM_I2C_MANAGER; serviceName HDF_PLATFORM_I2C_MANAGER; deviceMatchAttr hdf_platform_i2c_manager; } device1 :: deviceNode { policy 0; moduleName linux_i2c_adapter; deviceMatchAttr linux_i2c_adapter; } } } input_host :: host { hostName input_host; priority 100; device_touch :: device { device0 :: deviceNode { policy 2; preload 0; permission 0666; moduleName HDF_TOUCH_GT911; serviceName hdf_input_host0; deviceMatchAttr touch_device1; } } }注意 GPIO、I2C 都拆成了两个 DeviceNode一个是管理器policy 2对外发布一个是 Linux 适配policy 0不发布服务只给同 Host 的管理器当腿。你要是给 adapter 也写成 policy 2用户态能 Bind 到一个它不该直接用的对象调用约定全乱。7. policy 0 / 1 / 2用户态看得见看不见就看这个官方枚举比三个值多本教程日常只用前三个typedef enum { SERVICE_POLICY_NONE 0, /* 不发布服务 */ SERVICE_POLICY_PUBLIC 1, /* 只对内核态发布 */ SERVICE_POLICY_CAPACITY 2, /* 内核态 用户态都发布 */ SERVICE_POLICY_FRIENDLY 3, /* 不发布可被订阅 */ SERVICE_POLICY_PRIVATE 4, /* 私有不可订阅 */ } ServicePolicy;0谁 GetService 都没有。纯适配层比如上面的linux_i2c_adapter。它的存在是给管理器用的不是给应用用的。1内核态驱动之间能拿到。看门狗这类「内核自己喂、用户态不必插手」可以走 1。你要是把触摸写成 1内核日志里 Init 成功hdc 里HdfIoServiceBind却永远 NULL。应用同学开始怀疑 hap 权限、怀疑 SELinux、怀疑签名查三天最后发现是一个整数。2内核态和用户态都发布。触摸、GPIO 管理器、UART、ADC凡是 HDI 或者测试程序要找的都是 2。用户态拿服务的最小样子#include hdf_io_service_if.h int OpenInputHost(void) { struct HdfIoService *svc HdfIoServiceBind(hdf_input_host0); if (svc NULL) { /* 十有八九policy 不是 2或 serviceName 写错或 khdf 根本没编进内核 */ HDF_LOGE(bind hdf_input_host0 failed); return -1; } /* 后面用 svc-dispatcher-Dispatch(...) 发消息 */ HdfIoServiceRecycle(svc); return 0; }Bind 失败时不要先改应用。按这个顺序问户口本里有没有这个serviceNamepolicy是不是 2preload是不是 0或者有没有人先 GetService 过Image 里grep -a得到这个字符串吗这四问能消掉大半「应用层问题」。加载策略跟 policy 是两件事。preload 决定什么时候把驱动拉起来policy 决定拉起来之后服务给谁看。两个都写成你以为的「默认」框架的默认并不总是你以为的那个。模板里policy 0、preload 0继承时你忘了覆盖 policy设备会开机加载但不对外。看起来「驱动在 dmesg 里有用户态没有」就是这个组合。8. 驱动侧你要写的三个函数HDF 驱动不是module_initplatform_driver。入口是一张表#include hdf_device_desc.h #include hdf_log.h static int32_t SampleBind(struct HdfDeviceObject *deviceObject) { static struct IDeviceIoService service { .Dispatch SampleDispatch, }; if (deviceObject NULL) { return HDF_ERR_INVALID_OBJECT; } deviceObject-service service; return HDF_SUCCESS; } static int32_t SampleInit(struct HdfDeviceObject *deviceObject) { const struct DeviceResourceNode *node NULL; uint32_t busNum 0; if (deviceObject NULL) { return HDF_ERR_INVALID_OBJECT; } node deviceObject-property; if (node NULL) { HDF_LOGE(sample: no property, check deviceMatchAttr); return HDF_ERR_INVALID_OBJECT; } if (HdfDeviceResourceGetUint32(node, busNum, busNum) ! HDF_SUCCESS) { HDF_LOGE(sample: busNum missing); return HDF_FAILURE; } HDF_LOGI(sample init, busNum%u, busNum); return HDF_SUCCESS; } static void SampleRelease(struct HdfDeviceObject *deviceObject) { (void)deviceObject; } static struct HdfDriverEntry g_sampleEntry { .moduleVersion 1, .moduleName HDF_SAMPLE_MISC, .Bind SampleBind, .Init SampleInit, .Release SampleRelease, }; HDF_INIT(g_sampleEntry);HDF_INIT把这张表挂到框架的入口链表。moduleName必须等于户口本里的moduleName。调用顺序是先 Bind 后 Init。Bind 的职责是把服务接口挂到deviceObject-service上让框架能发布。Init 的职责是读配置、申请总线、注册中断。很多人把申请资源写进 Bind结果 Init 还没跑、配置还是空的申请出来的脚是 0。deviceObject-property就是deviceMatchAttr对上的那棵配置子树。它是空的百分之九十是 attr 字符串没对上不是 hc-gen 坏了。Dispatch 是用户态消息进来的门。policy2 时才会被用户态走到。内核态驱动互调走 GetService不走 Dispatch。这只是骨架。触摸、显示、音频的模型会替你把上报格式规定死芯片驱动填的是读寄存器和复位时序。HDF 驱动的「main」是这三个函数不是module_init。9. 同一条 I2C不要两边 bind这是 HDF 和 Linux 并存之后最具体的一条纪律。GT911 挂在 I2C2地址0x5d。内核树里有goodix驱动DTS 里要是写了gt9115d { compatible goodix,gt911; reg 0x5d; status okay; };内核就会去 probe。与此同时HDF 触摸模型按 HCS 的busNum 2也去找0x5d。两条路径没有协商只有先到先得。后果有三种都见过。第一种内核先到i2cdetect上0x5d显示UU被驱动占用。HDF 每一次 transfer 都 NACKhilog 刷i2c transfer failed。你以为芯片坏了其实芯片活得好好的只是被内核牵着走。第二种两边交替复位。HDF 拉一下 RST内核又按自己的时序拉一下。坐标乱跳偶发丢中断。第三种内核节点写了但驱动没编进来HDF 独占成功看起来「没问题」。过两周有人在 defconfig 里打开CONFIG_TOUCHSCREEN_GOODIXy触摸突然全死。你去比 HCSHCS 没变。变的是内核多了一个竞争者。正确写法是内核节点留着当备查status 必须 disabled总线留给 HDF。i2c2 { status okay; clock-frequency 100000; /* 这条总线上屏线和相机分支负载大400kHz 容易 NACK */ /* 备查用禁止 okay。HDF_TOUCH_GT911 独占 0x5d */ gt911_lvds: gt9115d { compatible goodix,gt911; reg 0x5d; interrupt-parent gpio0; interrupts RK_PC7 IRQ_TYPE_LEVEL_LOW; reset-gpios gpio2 RK_PD0 GPIO_ACTIVE_HIGH; status disabled; }; /* RGB 屏的 FT5406 同理HDF 独占 0x38 */ ft5406_rgb: touchscreen38 { compatible edt,edt-ft5406; reg 0x38; interrupt-parent gpio3; interrupts RK_PB2 IRQ_TYPE_LEVEL_LOW; reset-gpios gpio3 RK_PB1 GPIO_ACTIVE_LOW; status disabled; }; };超声波、RFID 反过来。它们不进桌面、不进 Kit外设测试应用 open/dev/hcsr04、/dev/nfc就够。DTSokay内核驱动 probeHCS 里不要再给同一地址登记一个 HDF 设备。判断用这三句比背表格快要进开源鸿蒙标准子系统——桌面触摸、composer、ADM、相机 Kit——走 HDF。只要一个/dev节点给外设测试应用读——走内核原生。两者都想要——选一边另一边 status disabled或者根本不要在 HCS 里登记。I2C 频率也是实打实的坑。公版参考把 i2c2 写成 400kHzFT5406 在这条总线上全部 NACK返回 -6。降到 100kHz 立刻 ACK。原因不是芯片「只支持 100k」是这条 FPC 加分支负载大边沿不够干净。HDF 和内核抢总线之前先保证电气上能 ACK。i2cdetect -y -r 2是听诊器不是装饰。10. 哪些走 HDF哪些不要硬塞结合这块 RK3568 的实际分工给你一个不会过时的名单。电容触摸 GT911、FT5406HDF Input 模型。上层要滑动解锁、要点图标。显示 panel 和 composerHDF Display 模型加 DRM。第 16 章起整篇都是它。音频HDF ADM。不要找/dev/snd。相机HDF / HDI 加 V4L2 / ISP第 59 章。底层传感器仍可能是内核 V4L2 子设备上面必须进相机框架应用才能走 Kit。超声波、RFID、红外避障、直流电机、步进电机、矩阵键盘gpio-matrix-keypad、LEDleds-gpio内核原生。这些没有标准 Kit 要接硬塞 Sensor 模型只会多写一堆 HCS外设测试应用还是得 open 节点。ADC 按键、PWM 风扇可以走 HDF 平台路径 B也可以走内核 iio / sysfs。本教程里风扇用 sysfs 演示足够ADC 按键用内核adc-keys更省事因为桌面本来就认 input 事件。选一条写进设备树或 HCS不要两条同时 okay。11. 改完怎么验收命令按这个顺序跑先确认当前启动盘。by-name 永远指 eMMC你以为自己在刷 SD可能写到了另一块介质。刷之前再防一次hdc shell cat /proc/partitions hdc shell mount | grep on / hdc shell cat /proc/version再看 HDF 用户态节点。policy2 的服务通常会在/dev/hdf_*出现hdc shell ls /dev/hdf_* hdc shell ls /proc/hdf/ 2/dev/null触摸起来之后多模输入侧能看到设备。名字不一定叫 gt911可能叫main_touch以 HCS 的devName为准hdc shell cat /proc/bus/input/devices hdc shell ls /sys/class/input/内核 goodix / edt不应该再绑 I2C。下面两条期望是「没有 driver 目录」或No such filehdc shell ls /sys/bus/i2c/devices/2-005d/driver hdc shell ls /sys/bus/i2c/devices/2-0038/driver扫总线。UU表示被占用5d/38表示有应答但没绑驱动--表示没人在hdc shell i2cdetect -y -r 2HDF 接管成功时0x5d常常是UU——占用者应当是 HDF 的 I2C 路径而不是goodix。结合上面ls .../driver一起看不要看见 UU 就高兴也不要看见 UU 就害怕。用户态 host 的话在 hilog内核 khdf 的话在 dmesg。两边都要看只看一边会漏hdc shell hilog -x hdc shell hilog | grep -iE hdf|touch|gt911|ft5406 hdc shell dmesg | grep -iE hdf|gt911|ft5406|touch|i2c刷完 khdf 如果行为没变回到主机做这三件事再决定要不要再刷一遍# 1. Image 里有没有新字符串 grep -a touch_device1 out/kernel/OBJ/linux-5.10/arch/arm64/boot/Image | head # 2. hex 源是不是今天生成的 ls -l vendor/rk/rk3568_evb/hdf_config/khdf/hdf_hcs_hex.c \ out/kernel/OBJ/linux-5.10/drivers/hdf/khdf/hdf_hcs_hex.c 2/dev/null # 3. 你刷的分区是不是 boot_linux路径是不是 /dev/block/ hdc shell ls -l /dev/block/mmcblk1p5 /dev/block/mmcblk0p5dd 必须写/dev/block/mmcblkXpY。写成/dev/mmcblkXpY这个路径常常不是块设备dd会在/dev下新建一个普通文件返回成功md5 也对重启还是旧内核。卷首语里写过这里再钉一次。12. 现象 → 原因下面这张表当听诊器不要当阅读顺序。现象对上了按「先查」那一列跑命令原因栏是我走过的那几条不是穷尽。现象先查常见原因改了 input_config.hcs 的 1024×600划屏仍按 800×1280 走Image 里 grep -a 新数字刷的是 boot_linux 还是 vendorkhdf 子文件改动没清 hcb链的是旧 hdf_hcs_hex.c驱动 Init 成功用户态 Bind 失败policy、serviceNamepolicy1 或名字少写一个 0i2cdetect 0x5d 是 UUHDF 报 I2C 失败/sys/bus/i2c/devices/2-005d/driver内核 goodix 抢了总线DTS 没 disabled只刷了 vendor触摸没变改的是 khdf 还是 uhdf触摸在 khdf要刷 boot_linux显示几何还要刷 resource开机没有 hdf 设备节点preload、moduleName、CONFIG_DRIVERS_HDF_*y模块没编进内核或 preload2 还没人 GetService改了 device_info.hcs 全量编译却行为旧ninja 是否重跑了内核 actionboard 目录不在内核 DEPS 里要删 checkpoint两个 Host 互相找不到服务两边的 priority被依赖的 Host 后加载property 为空Init 读不到 busNumdeviceMatchAttr 和 match_attr字符串差一个字符FT5406 全部 NACK返回 -6i2cdetect -y -r 2DTS clock-frequency400kHz 在长 FPC 上跑不稳降到 100kHz音频「没声卡」ls /dev/hdf_audio_*不要找 /dev/sndADM 不创建 ALSA 节点属正常背光不亮时序是对的PWM 号、lcd_config.hcs 的 pwmDevNumHDF 背光指错 PWM示波器量 PWM4「删 checkpoint」会和 hcb 缓存叠在一起。你清了 hcbninja 却因为 board 目录不在 DEPS 里根本没重跑内核hcb 清了也白清。核对一遍HDF 是开源鸿蒙自己的驱动框架RK3568 上它和 Linux 原生驱动并存并存就要谈所有权。khdf 编进内核uhdf 进 vendor。hc-gen 的 make 规则只盯顶层hdf.hcs改 include 的子文件必须删 hcb。用户态要看见服务policy必须是 2。触摸、音频、显示走 HDF超声波、RFID 走内核原生。GT911、FT5406 的内核节点保持disabled。想确认 make 会不会撒谎在input_config.hcs加一个debugTag hcs-cache-probe;不清 hcb编一次内核grep -a hcs-cache-probe那份 Image。再清 hcb 编一次。两次结果不一样缓存这件事就算看见了。系列第 13 篇 · 芯片瑞芯微 RK3568 · OpenHarmony 4.1API 11 · Linux 5.10