1. 嵌入式 HDMI1 光标卡死:从报错到定位渲染层
Failed to move cursor on screen HDMI1这个报错,第一次见的人容易以为是鼠标坏了或者 USB 接触不良。我在一块 ARM 开发板上调 Qt 界面时就遇到过:键盘能输入、程序能跑,唯独鼠标指针像被钉在屏幕左上角,动都不动,串口日志里反复刷这行提示。后来才明白,问题根本不在输入设备,而在显示输出这一层——EGLFS 把 HDMI1 当成了独立输出,但 KMS 配置里没给它分配硬件光标平面,光标自然没法跟着鼠标事件走。
先把概念捋清楚。KMS 是 Linux 内核的显示模式设置子系统,负责管理显卡的显示控制器、图层(plane)和连接器(connector)。EGLFS 是 Qt 在无桌面环境下的平台插件,它直接调用 DRM/KMS 接口把界面渲染到屏幕上,常见于嵌入式设备、工控屏、数字标牌这类场景。当 Qt 程序启动时,EGLFS 会读取一份 KMS 配置,决定用哪个/dev/dri/cardX、哪个输出口、什么分辨率,以及最关键的——是否启用硬件光标(hwcursor)。
报错里的HDMI1是内核给这个输出口起的名字,对应 DRM 里的HDMI-A-1。光标移动失败通常有三种诱因:一是hwcursor被打开,但当前 SoC 的显示控制器不支持在 HDMI 输出上叠加硬件光标平面;二是outputs列表里没写 HDMI1,EGLFS 找不到对应输出,光标事件无处投递;三是分辨率或pbuffers配置和实际硬件能力不匹配,导致渲染层初始化不完整。这三种情况在日志里的表现略有差异,但解决路径都指向同一份kms.conf。
适合读这篇的人:正在用 Qt + EGLFS 做嵌入式显示、手上有 HDMI 屏或转接板、被这行报错卡住的开发者。下面我会按「先备好统一 Key 环境 → 写 kms.conf → 配环境变量 → 验证光标 → 排错」的顺序走一遍,命令和配置都能直接复制。中间会用到 TaoToken 来统一管理模型调用的 Key,方便你在排查过程中随时让模型帮你读日志、解释报错,不用在多个平台之间来回切。
2. 用 TaoToken 统一 Key 管理排查环境
排查这类显示问题,很多时候需要一边看串口日志、一边让模型帮忙分析报错含义。如果每个模型都单独申请 Key、单独配环境变量,光是切换就够烦的。TaoToken 的思路是把多个模型的调用收敛到一个 API 入口,你只需要维护一份 Key,就能在排查脚本、终端工具、编辑器插件里复用。
它的 API 地址是https://taotoken.net/api,兼容常见的 OpenAI 风格调用格式。对嵌入式开发者来说,最实用的场景是:把串口抓到的日志片段丢给模型,让它判断是 KMS 配置问题还是驱动问题;或者让模型根据你的 SoC 型号生成一份候选的kms.conf。这些调用都走同一个 Key,不用为每个模型单独折腾。
先拿 Key。打开https://taotoken.net/api-keys,登录后创建一个 API Key,复制出来。注意这个 Key 只在创建时完整显示一次,建议直接写进你的 shell 配置文件里,别随手丢在聊天记录里。
拿到 Key 之后,配一个环境变量,后面所有调用都用它:
export TAOTOKEN_API_KEY="sk-你的Key"如果你习惯用 curl 快速验证,可以这样测一下 Key 是否可用:
curl https://taotoken.net/api/v1/models \ -H "Authorization: Bearer $TAOTOKEN_API_KEY"返回一个模型列表就说明 Key 没问题。这一步看着简单,但很多人后面排查到一半发现是 Key 拼错了或者过期了,白白浪费时间。先把这层确认掉,再往下走。
对于长期在嵌入式项目里做编码和 Agent 调试的,可以考虑 Coding Plan,它把常用的编码模型调用打包在一起,适合需要反复让模型读代码、读日志的场景。入口在https://taotoken.net/coding-plan。如果你只是想临时验证某个模型对日志的理解能力,用模型对话页面就够了:https://taotoken.net/models。
这里要强调一点:TaoToken 只是帮你统一模型调用的入口,它不参与你的显示渲染链路。kms.conf和 EGLFS 环境变量是本地 Qt 和内核的事,两者别混在一起理解。把 Key 环境备好,是为了后面排查时能随时借助模型,而不是让模型去改你的显示配置。
3. 可复制的 kms.conf 与 EGLFS 环境变量配置
现在进入正题。先确认你的设备节点和输出口名称。执行:
ls /dev/dri/通常会看到card0、card1以及renderD128之类。多数单 HDMI 的板子用card0就够。接着查输出口名字:
cat /sys/class/drm/card0-HDMI-A-1/status如果返回connected,说明 HDMI1 对应的 DRM 连接器是HDMI-A-1。注意报错里写的是HDMI1,而配置文件里要写HDMI-A-1,这两个名字不一样,写错了 EGLFS 就找不到输出。
创建/root/kms.conf,内容如下:
{ "device": "/dev/dri/card0", "hwcursor": false, "pbuffers": true, "outputs": [ { "name": "HDMI-A-1", "mode": "1920x1080" } ] }逐项说明。device指向你的 DRM 设备节点,多卡设备要确认 Qt 用的是哪一张。hwcursor设为false是解决光标问题的关键——关掉硬件光标后,EGLFS 会用软件方式绘制光标,虽然多占一点 CPU,但兼容性好得多,尤其在 SoC 的 HDMI 输出不支持独立光标平面时。pbuffers设为true让 EGLFS 使用 pbuffer 做离屏渲染,部分驱动在直接扫描输出时会有兼容问题,开这个能绕过去。outputs里显式声明HDMI-A-1和分辨率,避免 EGLFS 自动探测时选错模式。
如果你有多个输出口,比如同时接了 HDMI 和 LVDS,outputs数组里可以写多项,每项一个name和mode。但要注意,光标只会出现在被指定为主输出的那个屏幕上,多屏场景下光标跨屏移动需要额外配置,这里先不展开。
配好文件后,设置环境变量。可以临时在终端里 export,也可以写进启动脚本:
export QT_QPA_EGLFS_KMS_CONFIG="/root/kms.conf" export QT_QPA_EGLFS_INTEGRATION="eglfs_kms" export QT_QPA_PLATFORM="eglfs"QT_QPA_EGLFS_KMS_CONFIG告诉 EGLFS 去哪里读配置。QT_QPA_EGLFS_INTEGRATION指定用 KMS 集成后端,有些 Qt 版本默认不是它,显式写出来更稳。QT_QPA_PLATFORM设为eglfs,确保程序走 EGLFS 而不是 xcb 或 wayland。
如果你用的是 systemd 服务启动 Qt 程序,环境变量要写在 service 文件的Environment=里,或者用EnvironmentFile=引入一个 env 文件。别只在交互式 shell 里 export,服务启动时读不到。
另外,有些板子的 Qt 是通过qt5-launch或自定义启动脚本拉起的,确认脚本里没有覆盖这些变量。我见过有人配了kms.conf却没生效,最后发现是启动脚本里又 export 了一遍旧的QT_QPA_EGLFS_KMS_CONFIG,把新配置盖掉了。
4. 验证光标恢复与日志检查点
配置写完,重启 Qt 程序,观察串口日志。正常情况下,之前反复刷的Failed to move cursor on screen HDMI1应该消失。如果还在,先别急着改配置,按下面的检查点逐条过。
第一,确认环境变量真的传进了进程。程序启动后,在另一个终端执行:
cat /proc/$(pidof 你的程序名)/environ | tr '\0' '\n' | grep QT_QPA应该能看到QT_QPA_EGLFS_KMS_CONFIG=/root/kms.conf。如果看不到,说明启动方式没继承到变量,回去检查 service 文件或启动脚本。
第二,确认 EGLFS 读到了配置文件。Qt 在调试模式下会打印 KMS 配置的解析结果,启动时加QT_LOGGING_RULES="qt.qpa.eglfs.kms=true":
export QT_LOGGING_RULES="qt.qpa.eglfs.kms=true"重新启动程序,日志里会输出类似Loading KMS configuration from /root/kms.conf以及每个 output 的解析情况。如果提示找不到文件或 JSON 解析失败,检查路径和括号逗号。
第三,验证光标是否真的能动。写一个最小的 Qt 测试程序,或者直接用你现有的界面,把鼠标移到屏幕不同位置,观察指针是否跟随。如果指针出现了但移动卡顿,可能是软件光标绘制带来的开销,可以试着把分辨率降到 1280x720 看是否改善,以此判断是不是性能瓶颈。
第四,检查内核日志里有没有 DRM 相关报错:
dmesg | grep -i drm关注atomic、plane、cursor关键词。如果内核报no cursor plane available,那说明硬件确实不支持光标平面,hwcursor: false是唯一出路。如果报mode setting failed,则是分辨率或时序不匹配,需要调整mode字段。
实测下来,大部分Failed to move cursor的案例,把hwcursor关掉、outputs写对名字,就能解决。剩下的小部分,要么是设备节点选错,要么是 Qt 版本和 EGLFS 集成后端的兼容问题。
5. 常见报错对照排查
排查过程中会遇到几类典型报错,这里逐个对照。
Failed to move cursor on screen HDMI1持续刷屏:最常见。先确认hwcursor是否为false,再确认outputs里的name和/sys/class/drm/下的连接器名一致。如果都对了还在刷,检查是不是有多个 Qt 进程同时占用 DRM 设备,用fuser /dev/dri/card0看看。
Could not open DRM device /dev/dri/card0:权限或设备节点问题。确认当前用户对/dev/dri/card0有读写权限,嵌入式环境常用 root 跑,但如果是普通用户,需要加进video组。也可能是设备节点编号不是 card0,用ls /dev/dri/确认。
local proxy failed或连接类报错:这类通常出现在你用工具调用模型 API 时,和显示问题无关。检查TAOTOKEN_API_KEY是否设置正确,网络是否能到达https://taotoken.net/api。如果是公司内网,确认没有拦截。这类报错不要往 KMS 配置上想,分开排查。
401 Unauthorized:Key 无效或过期。重新在https://taotoken.net/api-keys生成一个,替换环境变量。注意别把 Key 里的字符复制漏了,尤其是开头和结尾。
reading choices相关报错:如果你在用某些 CLI 工具调模型,报错里出现reading choices通常是返回体格式和工具预期不一致。确认你用的模型 ID 和工具要求的格式匹配,必要时换一个模型试。
OAuth相关报错:部分工具用 OAuth 方式登录,如果你用的是 API Key 模式,需要在工具配置里切换认证方式,别混用。
光标出现但位置偏移:软件光标和实际触摸/鼠标坐标没对齐。检查mode分辨率是否和屏幕实际分辨率一致,以及 Qt 的QT_QPA_EGLFS_PHYSICAL_WIDTH、QT_QPA_EGLFS_PHYSICAL_HEIGHT是否设置合理。这两个变量影响坐标映射,设错了光标会偏。
程序启动后黑屏但无报错:可能是pbuffers配置和驱动不兼容。试着把pbuffers改成false再启动,对比日志。有些驱动在 pbuffer 模式下初始化失败但不报错,只是不显示。
排查时建议一次只改一个变量,改完重启看日志。同时改多个配置,出了问题很难定位是哪个起的作用。
6. 把模型接入排查流程:从日志到配置
前面几节的配置和命令,配合 TaoToken 的模型调用,可以把排查效率提上来。具体做法是:把串口日志、dmesg输出、kms.conf内容一起丢给模型,让它判断问题出在哪一层。调用方式用 curl 就行:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "你的模型ID", "messages": [ {"role": "user", "content": "以下是我的 kms.conf 和 dmesg 输出,请判断 HDMI1 光标无法移动的原因:\n<粘贴内容>"} ] }'模型 ID 可以在https://taotoken.net/models里查。如果你在编辑器里做嵌入式开发,想把模型接入编码流程,Coding Plan 的入口在https://taotoken.net/coding-plan,适合需要反复读代码、读日志的场景。接入文档在https://taotoken.net/doc,里面有各语言的调用示例。
需要提醒的是,模型能帮你分析日志、生成候选配置,但最终的kms.conf要落到你的实际硬件上验证。不同 SoC 的 DRM 驱动行为差异很大,模型给的配置只能当起点,不能当结论。我一般会让模型列出三到五个可能原因,然后按可能性从高到低逐个在设备上试。
另外,如果你在排查时用到了 Claude Code 这类工具,配置里需要写全三件套:Base URL 填https://taotoken.net/api,Key 填你的TAOTOKEN_API_KEY,Model ID 填你在模型列表里选的那个。三样缺一不可,少一个就会报认证或模型不存在的错。
最后说个实际经验:Failed to move cursor on screen HDMI1这类问题,九成出在hwcursor和outputs名字上。把这两个确认对,再配合QT_LOGGING_RULES看 EGLFS 的解析日志,基本能定位。剩下的边角情况,多半是驱动或 Qt 版本的老问题,升级或降级 Qt 的 EGLFS 插件有时比改配置更直接。排查完记得把有效的kms.conf和启动脚本一起归档,下次换板子能直接复用。