Linux下荣耀MagicBook的触控板被错误识别为鼠标的临时解决方案

发布时间 2023-11-19 11:56:34作者: 方而静

TL;DR

安装软件包 hid-tools,然后运行命令:

sudo hid-feature set -f 30000 3 $(sudo hid-feature list-devices | grep BLTP7853 | awk -F: '{print $1}')

问题现象

荣耀 MagicBook 笔记本安装了汇顶的触控板,此触控板在Linux下被识别为鼠标,导致无法使用触控板手势等功能。该触控板自称为 BLTP7853:00 347D:7853 Touchpad,识别码为 347d:7853。问题表现为此触控板被识别为了两个设备(见 /proc/bus/input/devices),且有 Touchpad 字样的设备无数据。

`/proc/bus/input/devices` 中的部分输出
I: Bus=0018 Vendor=347d Product=7853 Version=0100
N: Name="BLTP7853:00 347D:7853 Mouse"
P: Phys=i2c-BLTP7853:00
S: Sysfs=/devices/pci0000:00/0000:00:15.0/i2c_designware.0/i2c-1/i2c-BLTP7853:00/0018:347D:7853.0001/input/input11
U: Uniq=
H: Handlers=event7 mouse0 
B: PROP=0
B: EV=17
B: KEY=70000 0 0 0 0
B: REL=903
B: MSC=10

I: Bus=0018 Vendor=347d Product=7853 Version=0100
N: Name="BLTP7853:00 347D:7853 Touchpad"
P: Phys=i2c-BLTP7853:00
S: Sysfs=/devices/pci0000:00/0000:00:15.0/i2c_designware.0/i2c-1/i2c-BLTP7853:00/0018:347D:7853.0001/input/input12
U: Uniq=
H: Handlers=event8 mouse1 
B: PROP=5
B: EV=1b
B: KEY=e520 10000 0 0 0 0
B: ABS=2e0800000000003
B: MSC=20

分析

由于 /dev/input/eventX 无输出,此问题显然与 X11 或者 libinput 无关,而是一个内核漏洞。其中模块 hid_multitouch 是头号怀疑对象。

(以下分析来自 @nexplorer-3e)在 Windows 设备管理器中,也存在两个设备:I2C HID DeviceMicrosoft Input Configuration Device,且这两个设备的设备识别码均为 BLTP7853。在微软的文档中有关于此的说明:触摸板必需 HID 顶级集合 - 输入模式功能报告

触摸板必需 HID 顶级集合 - 输入模式功能报告 摘录

输入模式功能报告由主机传达给 Windows 精确式触摸板,用于指示应该将哪个顶级集合用于输入报告。 可将两个集合用于输入报告:鼠标集合和 Windows 精确式触摸板集合。
默认情况下,在进行冷启动/电源周期时,Windows 精确式触摸板应使用鼠标集合报告输入。 Windows 精确式触摸板在任何时候应仅使用一个给定集合报告数据,并且在从主机收到指示所需输入模式的相应功能报告后,应该只从另一个集合进行报告。
主机指定的用于输入模式的值(用法 0x52)决定了应该用于报告输入的集合。

输入模式值 输入报告
0 鼠标集合
3 Windows 精确式触摸板集合

主机可以在读取报表描述符后,随时向 Windows 精确式触摸板发出输入模式功能报告(包括可能通过活动集合报告数据时)。 如果报告数据时发生模式切换,则所有触点和按钮状态都应报告为向上弹起,并且所有使用该集合进行的报告都应停止。 在所有触点实际上都已向上弹起后,可以使用新指定的集合进行报告。 输入模式不应由 Windows 精确式触摸板跨电源周期或主机发起的重置(USB 重置、HID I2C HIR、HID SPI HIR)保留;但是输入模式可以跨任何设备发起的重置(例如 HID I2C DIR、HID SPI DIR 等)保留。

实验性解决方案

根据 @nexplorer-3e 提供的分析可知,我们只需将输入模式(Inputmode)设置为 3 即可。借助 hid-tools 工具(在我的发行版上可直接使用 pacman 安装),我们可以方便的对其进行设置,使用下面命令即可

sudo hid-feature set -f <Inputmode feature id> 3 <device node path>

其中 <Inputmode feature id> 替换为输入模式功能编号(似乎总是 30000),<device node path> 替换为触控板设备的路径即可。

获得触控板设备的路径和输入模式功能编号

首先使用如下命令得到设备路径:

sudo hid-feature list-devices | grep BLTP7853

可得到类似于如下的输出:

/dev/hidraw1: BLTP7853:00 347D:7853

这说明在我的系统中,触控板设备的路径为 /dev/hidraw1。然后使用命令

sudo hid-feature list /dev/hidraw0 | grep Inputmode

可得到类似于如下的输出:

 30000 |      0 | Digitizers                | Inputmode                                  | [ 0,  10] |     1 |   8

输入模式功能编号为 30000

解决方案

@nexplorer-3e 已经向 LKML 提交了补丁,不过此补丁尚未合并入内核主线。

我们可以手工编译 hid-multitouch 模块来将此补丁应用到当前的内核中。首先查看内核版本(uname -r),然后下载对应的内核源代码,注意通常需要从你的发行版而不是 kernel.org 上下载代码,因为发行版会应用一些额外的补丁。在我的例子中,我正在使用的内核是 6.6.1-1-MANJARO,它的构建脚本来自于 core/linux66

得到正在运行的内核的源代码后,打开 drivers/hid/hid-multitouch.c 并在注释 /* Ilitek dual touch panel */ 前(大约在 2050 行,取决于具体的内核版本,也可添加在其他合理位置,添加在此处只是为了保证按照设别的字典序排列)添加如下代码:

  /* HONOR GLO-GXXX panel */
  { .driver_data = MT_CLS_VTL,
    HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8,
      0x347d, 0x7853) },

然后获得正在运行的内核编译时的配置,这可以使用命令:

zcat /proc/config.gz > .config

并使用如下命令编译内核(请根据实际硬件情况调整编译线程数):

make prepare
make -j16

因为编译出的内核模块的 BTF 数据版本可能不能与正在运行的内核完全一致,故去除 BTF 信息:

objcopy --remove-section=.BTF drivers/hid/hid-multitouch.ko

最后使用编译出的模块替换原来的模块:

sudo modprobe -v -r hid-multitouch
sudo modprobe -v ./drivers/hid/hid-multitouch.ko
错误排查

如果 modprobe 报错,可使用命令

sudo dmesg

查看内核日志

确认无误后,可将模块文件放入系统目录以便在下次开机时自动装载:

zstd ./drivers/hid/hid-multitouch.ko -o hid-multitouch.ko.zst
cp /lib/modules/$(uname -r)/kernel/drivers/hid/hid-multitouch.ko.zst hid-multitouch.ko.zst.old # 可选,备份
sudo cp hid-multitouch.ko.zst /lib/modules/$(uname -r)/kernel/drivers/hid/

关于副作用

这会加载一个缺少签名,BTF的树外的模块进入内核,并使内核进入污染模式,这通常不是一个问题。

参考资料