![概要](https://linux22.com/image/120766/%E6%A6%82%E8%A6%81.png)
我昨天安装了一个新的 TrueOS 实例(FreeBSD 变体),并插入了 USB 键盘(Logitech G510)。它在安装环境中、首次启动设置期间以及直到新实例重新启动之前都运行良好。此时,它似乎完全停止发送输入,从 BSD 的启动时模块加载阶段识别它开始,因为那时 Scroll Lock 停止工作。但是,LCD 屏幕和背光灯打开,如果我插入另一个键盘,它会跟踪状态变化(Caps/NumLock 等),并且它在 Windows/Linux 和我的笔记本电脑上运行良好。
我尝试过在 BIOS 中打开/关闭 Legacy USB 和 Hand-off 的各种组合,以及使用不同的端口,但均无济于事。不同的 USB 键盘(下面日志中的“供应商 0x1241 USB 键盘”)无论哪种方式都可以正常工作。 USB 鼠标一直有效。没有 PS/2 端口。
知道这里发生了什么以及如何解决它吗?谢谢!
% dmesg | grep kb
kbd1 at kbdmux0
atkbdc0: <Keyboard controller (i8042)> at port 0x60,0x64 on isa0
atkbd0: <AT Keyboard> irq 1 on atkbdc0
kbd0 at atkbd0
atkbd0: [GIANT-LOCKED]
ukbd0 on uhub5
ukbd0: <vendor 0x1241 USB Keyboard, class 0/0, rev 1.10/2.80, addr 2> on usbus6
kbd2 at ukbd0
ukbd1 on uhub0
ukbd1: <Logitech G510 Gaming Keyboard, class 0/0, rev 2.00/1.65, addr 2> on usbus0
kbd3 at ukbd1
% ll /dev/*kb*
crw------- 1 root wheel 0x47 Oct 31 10:46 /dev/atkbd0
lrwxr-xr-x 1 root wheel 6 Oct 31 10:46 /dev/kbd0 -> atkbd0
lrwxr-xr-x 1 root wheel 7 Oct 31 10:46 /dev/kbd1 -> kbdmux0
lrwxr-xr-x 1 root wheel 5 Oct 31 10:46 /dev/kbd2 -> ukbd0
lrwxr-xr-x 1 root wheel 5 Oct 31 10:46 /dev/kbd3 -> ukbd1
crw------- 1 root wheel 0x25 Oct 31 10:46 /dev/kbdmux0
crw------- 1 root wheel 0x62 Oct 31 10:46 /dev/ukbd0
crw------- 1 root wheel 0x6a Oct 31 10:46 /dev/ukbd1
答案1
概要
发生的事情实际上非常简单:为了提供真正的 N 键无冲突以及所有这些额外的游戏和其他按键,罗技调整了通过 USB 报告键盘输入事件的方式。 FreeBSD 内核内置的 USB 输入报告解析库不支持此功能。如果您没有过滤内核消息输出,您会看到类似的消息
hid_get_item:项目数(991)被截断为 255 hid_get_item:项目数(257)被截断为 255或者
hid_get_item:364:项目数被截断为 255
细节
USB 输入报告协议允许 USB HID 拥有两组,每组最多 65531 个密钥。当前的 USB 标准定义了第一组中键 4 ( A a) 到 231 ( )的代码。 ⌘ Right GUI“游戏键盘”的按键数量比这多得多,其中大多数按键与标准化按键代码不对应,并且实际上无论如何都可能属于第二组。
USB HID从概念上来说将键盘活动报告为一个巨大的位图,每个键对应一位,描述键盘上所有键的当前向上/向下状态。虽然这确实是 8 个修饰键的情况,代码为 224 到 231,通常其他键以倒排数组的形式报告。从设备通过线路发送到 USB 主机的键盘输入报告包括指数位图中的位数被设置为 1。
这种倒排数组形式可以节省空间。我现在在我的 126 键多媒体键盘上输入此答案,每个输入报告需要 16 个字节才能以位图形式报告整个键盘。然而,对于倒排数组,它仅使用 8 个字节,其中 6 个是非修饰键的数组索引。
这种倒排数组形式的问题在于,人们不会不劳而获:
- 它限制了翻转。 有了完整的位图,就可以实现真正的 N 键翻转(受到键盘矩阵本身的任何硬件限制)。对于倒排数组形式,数组索引的数量限制了翻转的数量。传统上,由于 USB HID 标准实际上在附录中描述了这种“启动”报告格式,因此 USB HID 键盘在其报告中提供 6 个数组索引,允许同时按下最多 6 个(非修饰符)键。
- 它限制了关键代码。 数组索引通常是 8 位整数。使它们更宽,以便它们可以容纳超过 256 个键代码,并且要么需要超过 8 个字节来报告 6 个同时按下的(非修饰符)键,要么将可以报告的同时按下的键的数量减少到少于6.
不幸的是,游戏键盘需要能够报告远远超过 256 个键代码。从上述日志摘录中可以看出,罗技游戏键盘的按键代码在一种情况下最多为 257 个,在另一种情况下最多为 991 个。这并不意味着它有 257 和 991 个键。这意味着键码值的范围有那么宽吗?
FreeBSD 内核的限制,更具体地说是链接到 FreeBSD 内核的 libusb 精简版的限制,有些微妙。正如经常报道的那样,它们并不是位图大小的限制。它们是对 USB 术语中所谓的限制报告计数的一个输入项。 FreeBSD 将报告计数限制为 255,如果实际报告计数大于 255,则打印上述消息。
这会产生以下后果:两个都键盘输入的直位图形式和倒排数组形式。
- 对于直位图形式,这意味着大于 255 的位图将在第 255 个键码处被截断。即仅考虑位图的前 64 个字节。这限制了将被识别的键代码的范围。
- 对于倒排数组形式,这意味着仅使用前 255 个数组索引。当然,前 255 个数组索引可以包含任何键代码。这限制了有效的展期金额。
对于前者来说,这比后者更成问题。在重要的情况下,没有一个明智的人会使用倒排数组形式而不是位图形式。具有 255 个数组索引的倒排数组很多较少的与同等位图形式相比,空间效率更高,因为相同大小的位图可以表示 8 倍的键代码,并具有真正的 N 键翻转。
因此,罗技希望报告 257 或 991 键码范围内的任何键码,以使所有额外的键都能正常工作。 (为什么这样分割,与不同类型按键的代码被分组到不同的“页面”,以及输入项不能跨越多个“页面”的方式有关。这是USB的奥秘,超出了本文的范围)答案。)并且它希望为人们提供不止 6 键无滚转功能。
为此,它切换到 USB输入报告描述符格式使用位图和大量报告。 (USB 允许 HID 有多种输入报告格式,它们可以在这些格式之间进行切换。)FreeBSD 内核不喜欢这样,问题随之而来。
本地修复是强制 Logitech 键盘继续使用“启动”输入报告描述符格式,该格式提供 6 键翻转,并且无法发送键盘上任何额外按键的代码,而不是其“本机”报告描述符格式。这可以在每个系统引导程序中使用该命令完成usbconfig
,并为其提供add_quirk UQ_KBD_BOOTPROTO
子命令。
关键是将此怪癖放入 FreeBSD 内核中内置的现有预定义怪癖列表中,并由内核神奇地应用于匹配的 USB 设备。
服务修复是修复内核中的 libusb 削减,以便它不会将报告计数限制在 255。尽管在 FreeBSD 邮件列表之一上对此进行了简短而肤浅的讨论,但没有人这样做过。