← 返回文章列表

记录一次修复Linux下键盘网页驱动能识别却连不上的问题(迈从K99 V2)

tl;dr

简单来说及时手里的迈从K99 V2这个键盘在Windows上用网页驱动一切正常,换到Linux后,键盘没啥问题,但是在web driver中无法连接到键盘。浏览器弹窗能列出接收器,但选中设备后页面就一直转圈。

最后发现:接收器的配置接口默认只允许root访问,普通用户跑的Chrome没有权限访问。加一条udev规则,给当前桌面用户放行该接口的读写权限就可以了。

其他用WebHID的键盘、鼠标如果遇到类似现象,也能用如下的方案修复。

注:只适用于2.4g usb dongle或者usb直连的键盘,如果用蓝牙的不行

排查过程

先看一下看看设备的硬件 ID:

lsusb

我这边看到的是:

Bus 001 Device 002: ID 41e4:2001 Compx MCHOSE 2.4G Wireless

其中 41e4 是厂商 ID(VID),2001 是产品 ID(PID)。不同的键盘会有不同的值且不同的连接方案拿到的值也不一样,所以实际操作的时候以你为准

然后检查hidraw节点的USB属性:

for device_node in /dev/hidraw*; do
    [ -e "$device_node" ] || continue
    printf '\n%s\n' "$device_node"
    udevadm info --query=property --name="$device_node" |
        grep -E '^(ID_VENDOR_ID|ID_MODEL_ID|ID_MODEL|ID_USB_INTERFACE_NUM)='
done

可以发现,我的接收器对应了两个节点,分别是

设备节点USB 接口编号用途(猜测)
/dev/hidraw000普通键盘输入接口
/dev/hidraw101包含厂商配置通道
/dev/hidraw0
ID_MODEL=MCHOSE_2.4G_Wireless
ID_MODEL_ID=2001
ID_VENDOR_ID=41e4
ID_USB_INTERFACE_NUM=00

/dev/hidraw1
ID_MODEL=MCHOSE_2.4G_Wireless
ID_MODEL_ID=2001
ID_VENDOR_ID=41e4
ID_USB_INTERFACE_NUM=01

查看/sys/class/hidraw/hidraw1/device/report_descriptor,可以发现其中包含厂商自定义用途页0xff02字节 06 02 ff),并同时声明了输入和O输出,所以推测接口01就是用做配置的接口

python3 - <<'PY'
from pathlib import Path

for name in ('hidraw0', 'hidraw1'):
    path = Path('/sys/class/hidraw') / name / 'device/report_descriptor'
    print(name)
    print(path.read_bytes().hex(' '))
PY

部分内容如下

描述符字节HID 项目含义
06 02 ffUsage Page用途页为 0xff02,属于厂商自定义范围;数值按小端序存储
09 02 81 00Usage + Input声明设备向电脑发送的 Input Report
09 02 91 00Usage + Output声明电脑向设备发送的 Output Report

接下来查看权限

# 查看基础权限和 ACL
ls -l /dev/hidraw1
getfacl -p /dev/hidraw1
00:35:39 $ ls -l /dev/hidraw1
getfacl -p /dev/hidraw1
crw-rw----+ 1 root root 240, 1 Oct 20 00:08 /dev/hidraw1
# file: /dev/hidraw1
# owner: root
# group: root
user::rw-
# 如果你没有权限,下面这一行应该是没有的
user:aynakeya:rw-
group::---
mask::rw-
other::---

你也可以通过这段代码检查自己有没有权限,如果没有权限应该会爆出PermissionError,如果出现这个错误,那么基本就是权限问题了

python3 - <<'PY'
import os

device_node = '/dev/hidraw1'  # 替换为刚才确认的节点
fd = os.open(device_node, os.O_RDWR | os.O_NONBLOCK)
os.close(fd)
print('当前用户可以读写打开该接口')
PY

添加udev规则修复

创建或者编辑一个udev文件,你可以写在一起也可以分开写,其中41e4,2001啥的替换成你自己的id

/etc/udev/rules.d/70-webhid-keyboard.rules

这条规则会匹配该VID下面的所有hidraw接口,如果你不确定哪个interface是配置接口的话。

ACTION!="remove", SUBSYSTEM=="hidraw", KERNEL=="hidraw*", ATTRS{idVendor}=="41e4", ATTRS{idProduct}=="2001", TAG+="uaccess"

如果你知道,可以把interface也给加上。

ACTION!="remove", SUBSYSTEM=="hidraw", KERNEL=="hidraw*", ATTRS{idVendor}=="41e4", ATTRS{idProduct}=="2001", ENV{ID_USB_INTERFACE_NUM}=="01", TAG+="uaccess"

保存之后,重载一下udev,然后你就可以正常连接了,理论上不需要重新插拔usb

sudo udevadm control --reload-rules

Reference

  1. Linux Kernel HIDRAW - Raw HID Device Interface
  2. Chrome for Developers: Access HID devices on the Web (Dev Tips)
  3. MDN Web Docs: WebHID API
  4. systemd source: udev-builtin-uaccess.c
  5. freedesktop.org: udev — Dynamic device management
  6. systemd rules: 73-seat-late.rules.in
  7. udev Rules Matching Keys (ATTRS hierarchy)
  8. Flatpak Documentation: Sandbox Permissions
  9. Chrome WebHID Security and Privacy