☰
读懂HID Usage Tables 1.3:从Usage ID到描述符实战
2026/10/15 8:21:26 网站建设 项目流程

简介:《USB HID Usage Tables 1.3》是USB Implementers' Forum发布的人机交互设备使用表最新版本,覆盖1996年至2022年更新,适用于键盘、鼠标、游戏控制器、触摸屏等HID类设备开发,面向硬件工程师、驱动开发者与系统集成人员。文档系统定义了标准Usage ID、Usage Page、Report Descriptor结构,以及Boot Protocol等传输规则,帮助操作系统准确识别并处理设备输入。包体为1个PDF文件,大小约4.09MB,内容完整、便于查阅。该版本经微软、英特尔、谷歌、苹果、罗技等公司专家参与制定,并纳入多项HUT评审意见,权威性与实用性兼具。已有558人学习下载,适合需要实现跨平台兼容或进行USB外设底层开发的技术人员,可为设备描述符编写、输入报告解析及产品规范符合性测试提供直接依据。

1. 先聊清楚这份 HID Usage Tables 1.3:为什么 2022 年还要翻 20 年前的规范

干 USB HID 驱动、写设备固件的人,桌面上大多躺着两份文档:一份是 HID 规范本身,一份就是这份《USB HID Usage Tables 1.3》。后者是 2022 年 1 月发布的版本,把 1996 年到 2022 年间攒下的 Usage ID、Usage Page、设备类别补充全部收在一本字典里,1.3 这个版本号看着老,实际上合并了 101 到 107 号审查请求,还有显示器控制、POS 收款设备、电源设备、物理接口设备等一堆新类别。三年前我在调一个自定义旋钮设备,设备枚举正常但按键值始终不对,论坛翻遍无果,最后逐字节对照 1.3 里的 Consumer Page 才找到原因。它不是高深理论,而是一本给键盘、鼠标、触摸屏、游戏外设、VR 控制器分配身份证号码的字典,适合固件工程师、驱动开发者和做设备认证的人,也适合所有被 HID 描述符折磨过的人。

2. Usage Table 的结构:从 Usage Page 到 Usage ID 的查表逻辑

很多新手拿到这份文档,第一反应是从头翻到尾,翻到第三页就晕了。正确读法完全不同:先搞清楚 Usage Page 是“哪本字典”,再查 Usage ID 是“哪个词条”,最后查 Usage Type 判断“这个词条的行为属性”。

2.1 Usage Page 体系与 1.3 的边界

HID Usage Table 里最顶层的概念就是 Usage Page。它就像一个命名空间:同一个数值在不同 Page 下含义完全不同。比如 0x01 在 Generic Desktop Page 里是“Mouse”,但在 Button Page 里就是“Button 1”的起始值,在 Keyboard/Keypad Page 里则是“Esc”。所以描述符里 Usage Page 和 Usage 必须成对出现,单看 Usage ID 没有任何意义。

1.3 版本里常见 Page 分配可以快速过一遍:

Usage Page 值页面名称典型设备
0x01Generic Desktop Page键盘、鼠标、旋钮、方向键
0x02Simulation Controls Page飞行摇杆、赛车方向盘、坦克模拟
0x03VR Controls PageVR 手柄、头部追踪
0x04Sport Controls Page球拍、健身器材
0x05Game Controls Page游戏手柄、射击枪、弹球台
0x06Generic Device Controls Page设备背景控制、握持检测
0x07Keyboard/Keypad Page键盘按键矩阵
0x08LED Page键盘指示灯、RGB LED
0x09Button Page鼠标按键、通用按键
0x0AOrdinal Page顺序编号
0x0BTelephony Device Page电话机、呼叫控制
0x0CConsumer Page多媒体键、音量、播放控制
0x0DDigitizer Page触摸屏、笔、触控板

注意 1.3 里 0x06 Generic Device Controls 被大幅扩充了,里面新增了 Background/Nonuser Controls、Device Grip Controls、Versioning 这些子类。做笔记本触控板、带握持检测的手柄的开发者,之前只能把这些信息塞进厂商自定义区,现在表里给了标准位置,主机端就能统一解析。另外 0x03 VR Controls Page 也是近几个版本逐步完善的,里面定义了 VR 手柄上的摇杆、扳机、系统按键的标准化 ID。

2.2 Usage Type 决定设备行为

同一张表里每个 Usage 条目还带一个类型属性,这个属性经常被忽略,但它决定了驱动如何处理这个控件。文档 3.4 节把这套体系讲得很清楚,我挑几个开发中真正影响代码的:

Control 类里有 Linear Control(线性控制)、On/Off Control(开关)、Momentary Control(瞬间触发)、One Shot Control(单次触发)、Re-Trigger Control(重复触发)。鼠标 X 轴就是典型的 Linear Control,值是连续有方向的;按键是 Momentary Control,按下为 1 松开为 0;电话摘机这种动作更像 One Shot Control。

Data 类里有 Selector、Static Value、Dynamic Flag、Dynamic Value。Selector 表示当前位置从一组值里选一个,比如方向盘的角度和档位;Dynamic Value 是不断变化的数值,加速度计的输出就属于这一类。写驱动时,Momentary Control 通常直接映射为按键事件,Selector 则是查表映射,Static Value 往往表示一个配置项而不是输入事件。把类型看错,最直接的后果是同一个 usage 在你设备上表现为“一直按着”而不是“点一下”,主机端行为完全拧巴。

另外还有 Collection 类型,涉及 Named Array、Application、Logical、Physical 几类。Application Collection 表示一个完整的设备功能块,Physical Collection 表示空间上关联的一组控件。描述符里 Collection 嵌套层级错了,也会导致主机把一组按键当成多个独立设备来处理。

2.3 真正学会查表

我的查表习惯是三步走:第一步确认设备主用途,去对应的大类 Page 里定位;第二步找控件,看它的 Usage ID 落在哪一段;第三步看 Usage Type 和 Description 确认它的语义。举个实际例子,键盘上的 A 键在 0x07 Keyboard/Keypad Page 里 Usage ID 是 0x04,方向键左在同一个 Page 里是 0x50;鼠标左键在 0x09 Button Page 里 Usage ID 是 0x01;音量加在 0x0C Consumer Page 里是 0xE9。这几个值做 HID 的人应该闭着眼都能写出来,但如果你要做一个带滚轮且可下压的旋钮,就需要同时翻 0x01 Generic Desktop 里的 Axis Usages(0x30 是 X、0x31 是 Y、0x38 是 Wheel)和 0x09 Button Page 里的按键 ID。查表的关键是永远“先 Page 后 ID”,并且 1.3 里对同一个物理控件给出了明确的“推荐用途”,不要自创 ID 去撞保留区间。

3. 从表格到报告描述符:键盘与媒体键的逐字节拆解

规范是字典,描述符才是真正发给主机的“自我介绍”。这一章我把最常见两类设备的描述符逐字节拆开,对照 1.3 的 Usage Table 讲清楚每个值是从哪来的、能改什么、改错了会怎样。

3.1 一份标准 6 键无冲键盘描述符

下面这份描述符是很多 USB 键盘固件的标准模板,我加了详细注释。它对应 1.3 里 0x07 Keyboard/Keypad Page 的键盘键位表和 0x08 LED Page 的指示灯表:

// 6键无冲键盘报告描述符,Usage ID 均对照 HID Usage Tables 1.3 0x05, 0x01, // Usage Page (Generic Desktop) -- 全局:通用桌面页 0x09, 0x06, // Usage (Keyboard) -- 告诉主机这是一个键盘 0xA1, 0x01, // Collection (Application) -- 应用集合开始 // ---- 修饰键区:Ctrl/Shift/Alt/Win 等 8 个按键,每个占用 1 bit ---- 0x05, 0x07, // Usage Page (Keyboard/Keypad) -- 切到键盘页 0x19, 0xE0, // Usage Minimum (0xE0) -- 起始 Usage: Left Control 0x29, 0xE7, // Usage Maximum (0xE7) -- 结束 Usage: Right Meta 0x15, 0x00, // Logical Minimum (0) -- 逻辑最小值 0 0x25, 0x01, // Logical Maximum (1) -- 逻辑最大值 1 0x75, 0x01, // Report Size (1) -- 每个字段 1 bit 0x95, 0x08, // Report Count (8) -- 共 8 个字段 0x81, 0x02, // Input (Data, Variable, Absolute) -- 可读输入,可变 // ---- 保留字节:固定占 8 bit,让按键数组从整字节开始 ---- 0x95, 0x01, // Report Count (1) 0x75, 0x08, // Report Size (8) 0x81, 0x01, // Input (Constant) -- 常量,固定 0x00 // ---- LED 输出区:NumLock/CapsLock/ScrollLock 等 5 个指示灯 ---- 0x95, 0x05, // Report Count (5) 0x75, 0x01, // Report Size (1) 0x05, 0x08, // Usage Page (LED) -- 切到 LED 页 0x19, 0x01, // Usage Minimum (Num Lock) -- 0x01 是 Num Lock 0x29, 0x05, // Usage Maximum (Kana) -- 0x05 是 Kana 0x91, 0x02, // Output (Data, Variable, Absolute) -- 输出报告 // ---- LED 区剩余 padding:3 bit 常量 ---- 0x95, 0x01, // Report Count (1) 0x75, 0x03, // Report Size (3) 0x91, 0x01, // Output (Constant) // ---- 普通按键区:6 个按键槽位,每个 8 bit,值就是键位的 Usage ID ---- 0x95, 0x06, // Report Count (6) -- 支持 6 键同时按下 0x75, 0x08, // Report Size (8) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x65, // Logical Maximum (101) -- 0x65 = 101,键盘最大键位 0x05, 0x07, // Usage Page (Keyboard/Keypad) -- 切回键盘页 0x19, 0x00, // Usage Minimum (0) -- 0 代表无按键 0x29, 0x65, // Usage Maximum (101) -- 101 对应 1.3 表里最后一个键位 0x81, 0x00, // Input (Data, Array) -- 数组模式,值即 Usage ID 0xC0 // End Collection

这段描述符的逻辑是:先把设备声明为 Generic Desktop 下的 Keyboard 集合,然后依次描述三块数据——修饰键用位图表示,每个修饰键占 1 bit;LED 用输出报告控制;普通按键用数组表示,每个槽位填一个 Usage ID,0 表示没有按下。最后一段Input (Data, Array)是数组输入,与修饰键的Data, Variable不同:数组模式下同一时刻可以填多个值,主机按字节值直接查键盘表;Variable 模式下主机按 bit 位去找对应的 Usage ID。两种模式的解析逻辑完全不同,这也是新手写描述符最容易出问题的地方。

3.2 Consumer Page 媒体控制描述符

媒体键这类设备通常不需要 6 键无冲,它们要解决的是“同时只有一个动作”的场景。一个常见写法是把每个功能映射为 1 bit,报表大小 12 bit,一次最多表达 12 个媒体键中的任意一个:

// 媒体控制描述符:音量、播放控制,对照 0x0C Consumer Page 0x05, 0x0C, // Usage Page (Consumer) -- 消费类页面 0x09, 0x01, // Usage (Consumer Control) -- 消费控制集合 0xA1, 0x01, // Collection (Application) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) -- 每位取值 0 或 1 0x75, 0x01, // Report Size (1) -- 每功能占 1 bit 0x95, 0x0C, // Report Count (12) -- 共 12 个功能位 // 下面每行定义一个媒体键,Usage ID 都是 0x0C 页里查到的标准值 0x09, 0xE9, // Usage (Volume Increment) -- 音量加 0x09, 0xEA, // Usage (Volume Decrement) -- 音量减 0x09, 0xB5, // Usage (Next Track) -- 下一曲 0x09, 0xB6, // Usage (Previous Track) -- 上一曲 0x09, 0xB7, // Usage (Stop) -- 停止 0x09, 0xCD, // Usage (Play/Pause) -- 播放/暂停 0x09, 0xB8, // Usage (Eject) -- 弹出光盘 0x09, 0xB0, // Usage (Play) -- 播放 0x09, 0xB1, // Usage (Pause) -- 暂停 0x09, 0xB3, // Usage (Fast Forward) -- 快进 0x09, 0xB4, // Usage (Rewind) -- 快退 0x09, 0xB2, // Usage (Record) -- 录制 0x81, 0x02, // Input (Data, Variable, Absolute) -- 每位独立有效 0xC0 // End Collection

这段描述符里每个媒体键都是独立的 1 bit 字段,所以可以做到了即触发。注意 0x09, 0xCD 是 Play/Pause 的独立 Usage,它和 0xB0(Play)、0xB1(Pause)在表里是三个不同条目,说明书写时一定要先翻 Consumer Page 确认设备要暴露哪些标准键,而不是凭习惯用 Play 代替 Play/Pause。主机端对这两类键的处理策略不同,前者更接近媒体播放器的“切换播放状态”,后者是明确的开始或暂停。

3.3 字节序、Padding 与位宽对齐

描述符第三个常见问题是位宽对齐。HID 报告是按位打包的,不是按字节打包,所以每个 report 的位宽必须凑成整字节。上面键盘描述符里修饰键 8 bit、保留字节 8 bit、LED 5 bit 后面跟着 3 bit padding、普通按键 6 × 8 bit,全部字段加起来是 8 + 8 + 8 + 48 = 72 bit,正好 9 字节。如果你自己定义一个带触摸板的键盘,X 轴用 12 bit、Y 轴用 12 bit、压力用 6 bit,前面还有 8 bit 按键区,总位宽 38 bit,就必须补 2 bit padding 凑成 5 字节。很多设备在 Windows 上显示“HID 设备描述符请求失败”,原因就是 report 位宽不对齐。

逻辑最小值和最大值的类型也容易翻车。键盘普通按键区Logical Minimum 0, Maximum 101,这个 0 是有意义的:它表示“空槽位”。如果把 Logical Minimum 设成带符号数 -128,那数组模式下槽位值就可能被解析成负数,主机端查表直接错乱。1.3 表里键盘最大键位到 101,也就是 0x65,一般键盘只用 0x00 到 0x65 这段。设置 Logical Maximum 时不要顺手写 255,主机端按数组解析时会认为所有值都在表内,超出表范围的行为就不可控了。

4. 1.3 相比 1.2 更新了什么:显示器控制、电源设备与电话状态管理

这一章对已经有 1.2 使用经验的人最有用。1.3 不是简单修几个错别字,而是把一批长期散落在其他类别的 Usage 正式收编了进来。

4.1 版本更新概览

先看版本线,1.0 是 1997 年 10 月,1.1 是 1999 年 4 月,之后跳了 21 年才到 1.2(2020 年 7 月),紧接着 1.21(2020 年 10 月)、1.22(2021 年 4 月),最后是 2022 年 1 月的 1.3。1.3 合并的是 HUT Review Requests 101 到 107,引用文件里明确提到了 USB Monitor Control Class Specification、Open Arcade Architecture Device Data Format Specification、HID Point of Sale Usage Tables、HID Power Devices,以及 PID 物理接口设备规范。简单说,1.3 是把显示器控制、街机外设、POS 收款设备、电源设备和物理接口设备的 Usage 全部统一进了主表。

对各路开发者的意义也很直接:

开发者人群1.3 带来的变化
做 USB 显示器/扩展坞显示器亮度、对比度等控制有了标准 Usage,不再依赖厂商私有描述符
做收款机/扫码枪/钱箱POS 相关 Usage 进主表,多系统兼容不再靠厂商自定义
做充电器/电源适配器Power Device 的 Usage 表被引用进来,电池状态、充电控制有了统一 ID
做笔记本/平板Dockable Devices、Spatial Controls 等新子类补全了设备的扩展场景
做手柄/VR 外设1.3 补了 VR Controls Page 的大量细节,手柄按键映射更明确

4.2 显示器控制是这次更新的重头

1.3 里并入的 Monitor Control 类 Usage,让显示器亮度、对比度、输入源切换这些动作可以作为标准 HID 控件出现。以前做带 OSD 按键的显示器,厂商只能把这些功能放在 Vendor-Defined Usage Page 里,Windows 下自己写驱动或靠第三方工具才能调亮度;现在可以用通用 HID 驱动直接读取和设置。这个类别的落地场景是 USB 扩展坞和外接显示器:笔记本通过 USB-C 接显示器时,显示器厂商可以把亮度控制暴露成 HID Consumer Control,操作系统无需额外驱动就能支持亮度调节。

给固件工程师的建议是:如果产品做的是显示器 OSD 菜单,先到 1.3 的 Generic Desktop Page 4.10 System Display Controls 一节找亮度相关的控件,再决定哪些继续放厂商自定义区。标准区如果已经有对应控件,就不要再自造 ID,否则 Windows 更新后可能突然识别行为变化。

4.3 Resolution Multiplier 和 Feature Notifications 容易被忽略

1.3 里 Generic Desktop Page 保留了 Resolution Multiplier 这个特殊控件,它的存在感很低,但游戏外设非常需要。简单说,它是一个输出报告字段,用来把设备的物理分辨率乘以一个系数。游戏鼠标常见的做法是硬件报告 1000 CPI,实际需要 8000 CPI,过去是靠设备自己缩放,现在可以通过 Resolution Multiplier 让主机端配合完成。如果你在做高报告率的电竞鼠标,这个控件值得细看,它和 1.3 里 4.6 Buffered Bytes、4.8 Feature Notifications 是配套使用的,Feature Notifications 用来让主机主动通知设备“准备接收新的倍率值”。这两个部分在 1.2 里就已经存在,但直到 1.3 才被大量设备采用,因为显示器控制需要它们配合完成动态调节。

4.4 电话状态管理:做统一通信设备的要留意

4.16 Call State Management Control 是 1.3 里新补的重点。它给电话设备和 UC 客户端之间定义了一套标准 HID 用法,包含摘机、挂断、来电接听、通话保持、静音等状态。做话务耳机、桌面电话、USB 音视频一体机的厂商,之前这些状态都是通过私有 HID 报告或厂商驱动上报给客户端的,现在可以做到标准枚举。文档里给了两个示例,一个描述物理按键如何映射到 Call State,另一个描述如何通过 Feature Report 让主机读取当前通话状态。这类设备开发中容易被坑的地方是:电话状态不是单纯按键,它需要双向通信,所以要同时定义 Input(用户按下接听键)和 Feature(主机查询当前是否在通话中)两条路径。

5. 常见问题与排查:枚举失败、Usage 错位、描述符过长的处理

做 HID 设备的血泪经验,十有八九集中在枚举、Usage 分配和报告尺寸这三件事上。这里写五条我实际踩过、也帮别人排查过的坑,每一条都按“现象 → 原因 → 解决”讲清楚。

5.1 设备能枚举但按键完全错位

现象:插上设备后 Windows 和 Linux 都能识别出“USB 输入设备”,但按键输出完全对不上,按 A 出 B,按功能键出字母。

原因:最常见的是 Usage Page 和 Usage ID 不匹配。比如把音量键写进了 Generic Desktop Page 而不是 Consumer Page,主机收到 Usage ID 0xE9 时,在键盘页或桌面页里查不到对应含义,就会忽略或者乱映射。另一个原因是数组模式下 Logical Maximum 设置过小,超出范围的值被截断了。

解决:抓一次枚举时的 Report Descriptor,逐字节对照 1.3 的 Usage Table 查每个 Usage 的归属页。我一般会把描述符导出成十六进制文本,按 Chapter 3 的格式逐段拆开,确认 Page 切换的位置正确、每个 Usage ID 在对应 Page 里真实存在。特别注意:同一个用法值在不同 Page 下语义完全不同,检查时不能只看数字还要看前面的 Page 声明。

5.2 自定义设备在 Windows 上被当成鼠标

现象:设备是一个带滚轮的旋钮,但 Windows 把这个设备识别为鼠标,而 Linux 下行为正常。

原因:Windows 对 HID 的解析规则和 Linux 不完全一致。旋钮如果直接用了 Generic Desktop Page 里的 X 值(0x30)作为滚轮数据源,Windows 会很自然地把它归到鼠标类设备;Linux 的 hid-generic 驱动解析更宽松,不会立刻套用鼠标模板。另一个常见原因是报告里没有声明 Consumer Page 的 Consumer Control 集合,Windows 拿不到媒体键上下文,只能按桌面页的通用轴来处理。

解决:给旋钮设备同时声明 Generic Desktop 的 X、Y 轴和 Consumer Page 的 Consumer Control 集合,让 Windows 识别出这是一台多功能输入设备。商品化设备最好再实现一个 Feature Report 给主机查询设备能力,而不是让主机猜。

5.3 描述符超过 64 字节导致中断传输失败

现象:USB 全速设备中断传输最大包长 64 字节,描述符定义完,编译烧录后设备枚举失败,总线上一中断就报 STALL。

原因:报告描述符本身没有硬性长度上限,但中断 IN 端点的最大包长是有限制的。全速设备一般是 64 字节,高速设备可以到 1024 字节。如果你把所有按键、轴、LED、传感器都塞进一个报告,总长超过端点包长,传输自然失败。

解决:按功能拆报告,给每个逻辑功能块分配独立的 Report ID。键盘按键占一个 Report ID,媒体键占另一个,传感器数据占第三个。Host 通过 Report ID 区分当前收的是哪段数据,每段都控制在端点包长以内。我习惯把 Report ID 放在报告的第一个字节,值 1 到 255,枚举时通过描述符里的 Usage 关联。工程上还有一个折中技巧:把实时性要求高的输入放进短报告,把配置类数据放进 Feature Report,可以大幅度降低中断传输压力。

5.4 Logical Minimum 设成负数导致数据解析异常

现象:传感器设备输出加速度值,Linux 下用hidraw读到的数据符号不对,正数变负数,小值漂移严重。

原因:报告里逻辑最小值被定义成带符号数,比如Logical Minimum (-32768)、Logical Maximum (32767),而主机端驱动按无符号数解析,就会把超过 0x7FFF 的值当成负数处理,或者反过来。这类问题不是 Usage Table 的锅,是描述符里数值类型没和驱动约定一致。

解决:先明确数据的物理语义,再做类型选择。加速度计、陀螺仪这类本来就有方向的传感器,建议用带符号数,并且Report Size和Logical Maximum要覆盖量程;单纯的电量百分比、温度这种非负量,用无符号数,Logical Minimum设 0。描述符改完后,用hidrd-convert或者内核的hid_debug接口读一遍解析结果,确认数值符号与物理方向一致再批量烧录。

5.5 设备休眠唤醒后丢失功能

现象:设备正常工作,合盖休眠再打开,按键没响应,指示灯状态也丢了,必须重新插拔。

原因:设备没有正确处理主机发来的 Set_Report/Get_Report 请求。HID 规范里,LED 状态、传感器采样率这类配置是通过 Output Report 或 Feature Report 下发到设备的,休眠唤醒后主机不会主动重发这些配置,设备固件如果只在枚举时初始化一次,就会一直停留在错误的配置状态。

解决:设备固件里把配置信息持久化保存,每次枚举成功后在 Set Report 回调里更新配置;唤醒后主动对比上次保存的配置,不一致就重新加载。涉及传感器采样率、分辨率倍率这类运行时配置的,要同时实现 Feature Report 的 Get/Set 路径,并保证设备在总线复位后能恢复到默认枚举配置。这类坑在调试时最容易忽略,因为正常插拔永远复现不出来,只有不间断运行几小时后才会暴露。

5.6 没有总线分析仪时的三层排查法

没有逻辑分析仪也能排查大部分问题。我的排查顺序是:先看系统层是否识别,再看驱动层解析出的报告值,最后对比自己发的原始数据。Linux 下可以用usbhid的内核日志和hidraw设备节点来验证:

# 查看内核是否识别到 HID 设备,以及描述符解析结果 dmesg | grep -i hid # 列出 hidraw 设备节点,确认设备枚举成功 ls /dev/hidraw* # 用 hexdump 从 hidraw 读取原始报告,确认设备是否在发送数据 sudo cat /dev/hidraw0 | hexdump -C

正常工作的设备在cat /dev/hidraw0后不会有输出,因为报告只有事件产生时才发送;如果有连续输出,说明报告里某个字段没有处于空闲态,可能是有数据在不停刷新。收到报告后用第 6 章的脚本做对照检查,能快速定位 Usage 映射错误。如果手头有 Windows 机器,设备管理器里看“符合 HID 标准的用户控制设备”也是有效的旁证,这类设备节点出现后按键仍然无效,问题十有八九出在报告描述符本身。

6. 把一个日常习惯固化成脚本:描述符对表检查法

我的日常工作流里最后一步,是把描述符里用到的每一个 Usage Page 和 Usage ID 自动过一遍 1.3 的对照表。这个过程完全可以脚本化,最简单的版本只需要一个 Python 脚本。它的核心不是复杂算法,而是把规范里合法的 Page 和用法整理成数据,然后逐项校验描述符:

# 简易 HID Usage 对照检查脚本 # 实际项目里我会把 1.3 的键盘/Consumer/Button 表导出成 JSON,这里给出核心思路 known_pages = { 0x01: "Generic Desktop Page", 0x02: "Simulation Controls Page", 0x03: "VR Controls Page", 0x04: "Sport Controls Page", 0x05: "Game Controls Page", 0x06: "Generic Device Controls Page", 0x07: "Keyboard/Keypad Page", 0x08: "LED Page", 0x09: "Button Page", 0x0A: "Ordinal Page", 0x0B: "Telephony Device Page", 0x0C: "Consumer Page", 0x0D: "Digitizer Page", 0x0F: "Physical Interface Device Page", 0x12: "Game Player Page", } def check_usage(page, usage): # 先检查 Page 是否存在 if page not in known_pages: return f"警告:Page 0x{page:02X} 不在常见表内,确认是否为厂商自定义页" # 实际工程中这里还会查每个 Page 下的 usage 白名单 return f"正常:Page 0x{page:02X} ({known_pages[page]}) 已收录,继续检查 Usage 0x{usage:02X}" # 模拟从描述符里提出来的一组 (Page, Usage) 值 desc_usages = [(0x01, 0x06), (0x07, 0x04), (0x0C, 0xE9), (0xFF, 0x01)] for page, usage in desc_usages: print(check_usage(page, usage))

这个脚本的参数含义很直白:page是描述符里全局段声明的 Usage Page,usage是当前控件的 Usage ID。工程化使用时会直接把 1.3 里的键盘表、Button 表、Consumer 表导出成 JSON 文件,再配合一个报告描述符解析器,把字节流里的每个 Usage 提取出来自动对表。脚本跑完输出两类结果:一类是“不在常见表内”的警告,说明该值落在厂商自定义区间;另一类是“已收录”,直接对应到 1.3 的具体章节。

保存一个约定:每次写新描述符,编译前先跑一遍这个脚本,再联机验证。从那以后,我每次接一块新的输入设备固件,都会花十分钟把对应 Page 的 usage 白名单导进脚本,让它在编译前自动扫一遍。这个动作救过我很多次,尤其是熬夜调新板子的深夜,总线抓包看到的是一堆十六进制,脚本直接告诉你“这个值不是合法的 Volume Increase”,比事后翻设备管理器错报省心得多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询