键盘和鼠标 HID 客户端驱动程序
本文讨论键盘和鼠标 HID 客户端驱动程序。 键盘和鼠标表示在 HID 使用情况表中标准化并在 Windows 操作系统中实现的第一组 HID 客户端。
键盘和鼠标 HID 客户端驱动程序以 HID 映射器驱动程序的形式实现。 HID 映射器驱动程序是内核模式 WDM 筛选器驱动程序,它为非 HID 类驱动程序与 HID 类驱动程序之间的 I/O 请求提供双向接口。 映射器驱动程序将一个的 I/O 请求和数据协议映射到另一个。
Windows 为 HID 键盘和 HID 鼠标设备提供系统提供的 HID 映射器驱动程序。
体系结构和概述
下图演示了适用于 USB 键盘、鼠标和触摸板设备的系统提供的驱动程序堆栈。
该图显示了以下组件:
- KBDHID.sys:适用于键盘的 HID 客户端映射器驱动程序。 将 HID 用法转换为扫描代码,以与现有键盘类驱动程序进行交互。
- MOUHID.sys:用于鼠标/触摸板的 HID 客户端映射器驱动程序。 将 HID 用法转换为鼠标命令, (X/Y、按钮、滚轮) 来与现有键盘类驱动程序进行交互。
- KBDCLASS.sys: 键盘类驱动程序 以安全的方式为系统上的所有键盘和键盘提供功能。
- MOUCLASS.sys: 鼠标类驱动程序 为系统上的所有鼠标和触摸板提供功能。 驱动程序支持绝对和相对指向设备。 MOUCLASS.sys 不是触摸屏的 Windows 驱动程序。
- HIDCLASS.sys: HID 类驱动程序。 HID 类驱动程序是 KBDHID.sys 和 MOUHID.sys HID 客户端与各种传输(如 USB、蓝牙等)之间的粘附。
系统按如下所示生成驱动程序堆栈:
- 传输堆栈为附加的每个 HID 设备创建物理设备对象 (PDO) ,并加载相应的 HID 传输驱动程序,后者又加载 HID 类驱动程序。
- HID 类驱动程序为每个键盘或鼠标 TLC 创建 PDO。 (多个 TLC) 的复杂 HID 设备公开为 HID 类驱动程序创建的多个 PDO。 例如,具有集成鼠标的键盘可能具有一个用于标准键盘控件的集合,而鼠标的集合可能具有不同的集合。
- 键盘或鼠标 HID 客户端映射器驱动程序加载到相应的 FDO 上。
- HID 映射器驱动程序为键盘和鼠标创建 FDO,并加载类驱动程序。
重要事项
- 符合支持的 HID 用法和顶级集合的键盘和鼠标不需要供应商驱动程序。
- 供应商可以选择在 HID 堆栈中提供筛选器驱动程序,以更改/增强这些特定 TLC 的功能。
- 供应商应创建单独的特定于供应商的 TLC,以在其 HID 客户端和设备之间交换专有数据。 除非关键,否则请避免使用筛选器驱动程序。
- 系统将打开所有键盘和鼠标集合供其独占使用。
- 系统阻止禁用/启用键盘。
- 系统支持水平/垂直滚轮,具有平滑滚动功能。
驱动程序指南
Microsoft 为编写驱动程序的 IHV 提供了以下指南:
驱动程序开发人员可以筛选器驱动程序或新的 HID 客户端驱动程序的形式添加更多驱动程序。
筛选器驱动程序:驱动程序开发人员应确保其增值驱动程序是筛选器驱动程序,并且不会替换 (或用于代替输入堆栈中) 现有 Windows HID 驱动程序。
- 在以下情况下,允许使用筛选器驱动程序:
- 作为 kbdhid/mouhid 的上部筛选器
- 作为 kbdclass/mouclass 的上部筛选器
- 不建议 将 筛选器驱动程序用作 HIDCLASS 和 HID 传输微型驱动程序之间的筛选器
- 在以下情况下,允许使用筛选器驱动程序:
函数驱动程序:或者,供应商可以创建函数驱动程序 (而不是筛选器驱动程序) ,但仅针对供应商特定的 HID PDO (用户模式服务(如有必要)) 。
在以下情况下,允许使用函数驱动程序:
- 仅在特定供应商的硬件上加载
传输驱动程序:Windows 团队不建议创建更多 HID 传输微型驱动程序。 它们是需要编写和维护的复杂驱动程序。 如果合作伙伴正在创建新的 HID 传输微型驱动程序(尤其是在 SoC 系统上),我们建议进行详细的体系结构评审,以了解推理并确保正确开发驱动程序。
驱动程序开发人员应 (KMDF 或 UMDF) 使用驱动程序框架,而不是依赖 WDM 来获取其筛选器驱动程序。
驱动程序开发人员应减少其服务和驱动程序堆栈之间的内核用户转换次数。
驱动程序开发人员应确保能够通过键盘和触摸板功能唤醒系统, (最终用户 (设备管理器) 或电脑制造商) 进行调整。 此外,在 SoC 系统上,当系统处于工作 S0 状态时,这些设备必须能够将自身从低功率状态唤醒。
驱动程序开发人员应确保其硬件进行高效电源管理。
- 当设备处于空闲状态时,设备可以进入其最低功率状态。
- 当系统处于低功耗状态时,设备处于最低功率状态, (例如,待机 (S3) 或连接的待机) 。
键盘布局
键盘布局完全描述了 Microsoft Windows 2000 及更高版本的键盘输入特征。 例如,键盘布局指定语言、键盘类型和版本、修饰符、扫描代码等。
有关键盘布局的信息,请参阅以下资源:
Windows 驱动程序开发工具包中的键盘头文件 kdb.h (DDK) ,其中记录了有关键盘布局的一般信息。
示例键盘 布局。
若要可视化特定键盘的布局,请参阅 Windows 键盘布局。
有关键盘布局的更多详细信息,请访问 控制面板\Clock、Language 和 Region\Language。
鼠标上支持的按钮和方向盘
下表确定了 Windows 操作系统的不同客户端版本支持的功能。
功能 | Windows XP | Windows Vista | Windows 7 | Windows 8 及更高版本 |
---|---|---|---|---|
按钮 1-5 | 支持的 (P/2 & HID) | 支持的 (PS/2 & HID) | 支持的 (PS/2 & HID) | 支持的 (PS/2 & HID) |
垂直滚轮 | 支持的 (PS/2 & HID) | 支持的 (PS/2 & HID) | 支持的 (PS/2 & HID) | 支持的 (PS/2 & HID) |
水平滚轮 | 不支持 | 仅) 支持 (HID | 仅) 支持 (HID | 仅) 支持 (HID |
水平和垂直) (平滑滚轮支持 | 不支持 | 部分支持 | 仅支持 (HID) | 仅支持 (HID) |
激活 PS/2 鼠标上的按钮 4-5 和滚轮
Windows 用于激活新的四按钮和五按钮和滚轮模式的方法是用于在 IntelliMouse 兼容鼠标中激活第三个按钮和滚轮的方法的扩展:
通过将报告速率设置为每秒 200 个报告,然后设置为每秒 100 个报告,然后设置为每秒 80 个报告,将鼠标设置为三按钮滚轮模式。 然后,从鼠标读取 ID。 完成此序列时,鼠标应报告 ID 为 3。
接下来,通过将报告速率设置为每秒 200 个报告,然后再次设置为每秒 200 个报告,然后设置为每秒 80 个报表,将鼠标设置为五按钮滚轮模式。 然后,从鼠标读取 ID。 序列完成后,五按钮滚轮鼠标应报告 ID 为 4 (而与 IntelliMouse 兼容的三按钮滚轮鼠标仍将报告 ID 为 3) 。
此方法仅适用于 PS/2 小鼠,不适用于 HID 小鼠。 HID 鼠标必须在其报告描述符中报告准确的使用情况。
标准 PS/2 兼容鼠标数据包格式 (两个按钮)
Byte | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 评论 |
---|---|---|---|---|---|---|---|---|---|
1 | Yover | Xover | Ysign | Xsign | 标记 | M | R | L | X/Y 溢出和符号、按钮 |
2 | X7 | X6 | X5 | X4 | X3 | X2 | X1 | X0 | X 数据字节 |
3 | Y7 | Y6 | 是5 | Y4 | Y3 | 是2 | 是1 | Y0 | Y 数据字节 |
注意
Windows 鼠标驱动程序不会检查溢出位。 发生溢出时,鼠标应仅发送最大有符号位移值。
标准 PS/2 兼容鼠标数据包格式 (三个按钮 + 垂直滚轮)
Byte | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 评论 |
---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | Ysign | Xsign | 1 | M | R | L | X/Y 符号和 R/L/M 按钮 |
2 | X7 | X6 | X5 | X4 | X3 | X2 | X1 | X0 | X 数据字节 |
3 | Y7 | Y6 | 是5 | Y4 | Y3 | 是2 | 是1 | Y0 | Y 数据字节 |
4 | Z7 | Z6 | Z5 | Z4 | Z3 | Z2 | Z1 | Z0 | Z/wheel 数据字节 |
标准 PS/2 兼容鼠标数据包格式 (五个按钮 + 垂直滚轮)
Byte | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 评论 |
---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | Ysign | Xsign | 1 | M | R | L | X/Y 符号和 R/L/M 按钮 |
2 | X7 | X6 | X5 | X4 | X3 | X2 | X1 | X0 | X 数据字节 |
3 | Y7 | Y6 | 是5 | Y4 | Y3 | 是2 | 是1 | Y0 | Y 数据字节 |
4 | 0 | 0 | B5 | B4 | Z3 | Z2 | Z1 | Z0 | Z/wheel 数据和按钮 4 和 5 |
重要
请注意,五按钮滚轮鼠标的 Z/wheel 数据已减少到 4 位,而不是 IntelliMouse 兼容的三按钮滚轮模式中使用的 8 位。 由于在任何给定中断期间,滚轮通常无法生成超出 +7/-8 范围的值,因此可以实现这种缩减。 当鼠标处于五按钮滚轮模式时,Windows 鼠标驱动程序将签署扩展四个 Z/wheel 数据位,当鼠标在三按钮滚轮模式下运行时,将签署完整的 Z/wheel 数据字节。
上的按钮 4 & 5 映射到WM_APPCOMMAND消息,并对应于App_Back和App_Forward。
不需要供应商驱动程序的设备
以下设备不需要供应商驱动程序:
- 符合 HID 标准的设备。
- 由系统提供的非 HIDClass 驱动程序操作的键盘、鼠标或游戏端口设备。
Kbfiltr 示例
Kbfiltr 与 Kbdclass(键盘设备的系统类驱动程序)和 I8042prt(PS/2 样式键盘的功能驱动程序)一起使用。 Kbfiltr 演示如何筛选 I/O 请求以及如何添加用于修改 Kbdclass 和 I8042prt 操作的回调例程。
有关 Kbfiltr 操作的详细信息,请参阅:
Kbfiltr IO 控制代码
Kbfiltr 使用以下 IOCTL。
IOCTL_INTERNAL_I8042_HOOK_KEYBOARD
IOCTL_INTERNAL_I8042_HOOK_KEYBOARD请求:
- 将初始化回调例程添加到 I8042prt 键盘初始化例程。
- 将 ISR 回调例程添加到 I8042prt 键盘 ISR。
初始化和 ISR 回调是可选的,由 PS/2 样式键盘设备的上层筛选器驱动程序提供。
I8042prt 收到 IOCTL_INTERNAL_KEYBOARD_CONNECT 请求后,会将同步 IOCTL_INTERNAL_I8042_HOOK_KEYBOARD 请求发送到键盘设备堆栈的顶部。
在 Kbfiltr 收到挂钩键盘请求后,Kbfiltr 按以下方式筛选请求:
- 保存传递给 Kbfiltr 的上层信息,其中包括高级设备对象的上下文、指向初始化回调的指针和指向 ISR 回调的指针。
- 将上层信息替换为其自己的信息。
- 保存 I8042prt 的上下文和指向 Kbfiltr ISR 回调可以使用的回调的指针。
IOCTL_INTERNAL_KEYBOARD_CONNECT
IOCTL_INTERNAL_KEYBOARD_CONNECT请求将 Kbdclass 服务连接到键盘设备。 Kbdclass 在打开键盘设备之前,将此请求发送到键盘设备堆栈。
在 Kbfiltr 收到键盘连接请求后,Kbfiltr 按以下方式筛选连接请求:
- 保存 Kbdclass CONNECT_DATA (Kbdclass) 结构的副本,该结构由 Kbdclass 传递给筛选器驱动程序。
- 将自己的连接信息替换为类驱动程序连接信息。
- 在设备堆栈中向下发送 IOCTL_INTERNAL_KEYBOARD_CONNECT 请求。
如果请求不成功,Kbfiltr 会以适当的错误状态完成请求。
Kbfiltr 为筛选器服务回调例程提供了一个模板,该模板可以补充 KeyboardClassServiceCallback(Kbdclass 类服务回调例程)的操作。 筛选器服务回调可以筛选从设备输入缓冲区传输到类数据队列的输入数据。
IOCTL_INTERNAL_KEYBOARD_DISCONNECT
IOCTL_INTERNAL_KEYBOARD_DISCONNECT请求已完成,状态为STATUS_NOT_IMPLEMENTED。 即插即用管理器可以添加或删除即插即用键盘。
对于所有其他设备控制请求,Kbfiltr 会跳过当前 IRP 堆栈,并在设备堆栈中向下发送请求,而无需进一步处理。
Kbfiltr 实现的回调例程
Kbfiltr 实现以下回调例程。
KbFilter_InitializationRoutine
请参阅 PI8042_KEYBOARD_INITIALIZATION_ROUTINE
如果键盘的 I8042prt 默认初始化已足够,则不需要 KbFilter_InitializationRoutine 。
I8042prt 在初始化键盘时调用 KbFilter_InitializationRoutine 。 默认键盘初始化包括以下操作:
- 重置键盘
- 设置类型速率和延迟
- 将发光二极管 (LED)
/*
Parameters
DeviceObject [in]
Pointer to the device object that is the context for this callback.
SynchFuncContext [in]
Pointer to the context for the routines pointed to by ReadPort and Writeport.
ReadPort [in]
Pointer to the system-supplied PI8042_SYNCH_READ_PORT callback that reads from the port.
WritePort [in]
Pointer to the system-supplied PI8042_SYNCH_WRITE_PORT callback that writes to the port.
TurnTranslationOn [out]
Specifies, if TRUE, to turn translation on. Otherwise, translation is turned off.
Return value
KbFilter_InitializationRoutine returns an appropriate NTSTATUS code.
*/
NTSTATUS KbFilter_InitializationRoutine(
In PDEVICE_OBJECT DeviceObject,
In PVOID SynchFuncContext,
In PI8042_SYNCH_READ_PORT ReadPort,
In PI8042_SYNCH_WRITE_PORT WritePort,
Out PBOOLEAN TurnTranslationOn
);
KbFilter_IsrHook
请参阅 PI8042_KEYBOARD_ISR。 如果 I8042prt 的默认操作足够,则不需要此回调。
I8042prt 键盘 ISR 在验证中断并读取扫描代码后调用 KbFilter_IsrHook 。
KbFilter_IsrHook 在 I8042prt 键盘的 IRQL 的内核模式下运行。
/*
Parameters
DeviceObject [in]
Pointer to the filter device object of the driver that supplies this callback.
CurrentInput [in]
Pointer to the input KEYBOARD_INPUT_DATA structure that is being constructed by the ISR.
CurrentOutput [in]
Pointer to an OUTPUT_PACKET structure that specifies the bytes that are being written to the hardware device.
StatusByte [in, out]
Specifies the status byte that is read from I/O port 60 when an interrupt occurs.
DataByte [in]
Specifies the data byte that is read from I/O port 64 when an interrupt occurs.
ContinueProcessing [out]
Specifies, if TRUE, to continue processing in the I8042prt keyboard ISR after this callback returns; otherwise, processing is not continued.
ScanState [in]
Pointer to a KEYBOARD_SCAN_STATE structure that specifies the keyboard scan state.
Return value
KbFilter_IsrHook returns TRUE if the interrupt service routine should continue; otherwise it returns FALSE.
*/
KbFilter_IsrHook KbFilter_IsrHook(
In PDEVICE_OBJECT DeviceObject,
In PKEYBOARD_INPUT_DATA CurrentInput,
In POUTPUT_PACKET CurrentOutput,
Inout UCHAR StatusByte,
In PUCHAR DataByte,
Out PBOOLEAN ContinueProcessing,
In PKEYBOARD_SCAN_STATE ScanState
);
KbFilter_ServiceCallback
请参阅 PSERVICE_CALLBACK_ROUTINE。
函数驱动程序的 ISR 调度完成例程调用 KbFilter_ServiceCallback,然后调用键盘类驱动程序 实现PSERVICE_CALLBACK_ROUTINE。 供应商可以实现筛选器服务回调,以修改从设备的输入缓冲区传输到类数据队列的输入数据。 例如,回调可以删除、转换或插入数据。
/*
Parameters
DeviceObject [in]
Pointer to the class device object.
InputDataStart [in]
Pointer to the first keyboard input data packet in the input data buffer of the port device.
InputDataEnd [in]
Pointer to the keyboard input data packet that immediately follows the last data packet in the input data buffer of the port device.
InputDataConsumed [in, out]
Pointer to the number of keyboard input data packets that are transferred by the routine.
Return value
None
*/
VOID KbFilter_ServiceCallback(
In PDEVICE_OBJECT DeviceObject,
In PKEYBOARD_INPUT_DATA InputDataStart,
In PKEYBOARD_INPUT_DATA InputDataEnd,
Inout PULONG InputDataConsumed
);
Moufiltr 示例
Moufiltr 与 Mouclass(用于 Windows 2000 及更高版本的鼠标设备的系统类驱动程序)和 I8042prt(用于 Windows 2000 及更高版本的 PS/2 样式鼠标的函数驱动程序)配合使用。 Moufiltr 演示了如何筛选 I/O 请求并添加用于修改 Mouclass 和 I8042prt 操作的回调例程。
有关 Moufiltr 操作的详细信息,请参阅以下资源:
Moufiltr IO 控制代码
Moufiltr 使用以下 IOCTL。
IOCTL_INTERNAL_I8042_HOOK_MOUSE
IOCTL_INTERNAL_I8042_HOOK_MOUSE请求将 ISR 回调例程添加到 I8042prt 鼠标 ISR。 ISR 回调是可选的,由上层鼠标筛选器驱动程序提供。
I8042prt 在收到 IOCTL_INTERNAL_MOUSE_CONNECT 请求后发送此请求。 I8042prt 将同步 IOCTL_INTERNAL_I8042_HOOK_MOUSE 请求发送到鼠标设备堆栈的顶部。
Moufiltr 收到挂钩鼠标请求后,按以下方式筛选请求:
- 保存传递给 Moufiltr 的上层信息,其中包括上层设备对象的上下文和指向 ISR 回调的指针。
- 将上层信息替换为自己的信息。
- 保存 I8042prt 的上下文和指向 Moufiltr ISR 回调可以使用的回调的指针。
IOCTL_INTERNAL_MOUSE_CONNECT
IOCTL_INTERNAL_MOUSE_CONNECT请求将 Mouclass 服务连接到鼠标设备。
IOCTL_INTERNAL_MOUSE_DISCONNECT
Moufiltr 完成IOCTL_INTERNAL_MOUSE_DISCONNECT请求,错误状态为STATUS_NOT_IMPLEMENTED。
对于所有其他请求,Moufiltr 会跳过当前 IRP 堆栈,并在设备堆栈中向下发送请求,而无需进一步处理。
Moufiltr 回调例程
以下回调例程由 MouFiltr 实现。
MouFilter_IsrHook
请参阅 PI8042_MOUSE_ISR。
/*
Parameters
DeviceObject
Pointer to the filter device object of the driver that supplies this callback.
CurrentInput
Pointer to the input MOUSE_INPUT_DATA structure being constructed by the ISR.
CurrentOutput
Pointer to the OUTPUT_PACKET structure that specifies the bytes being written to the hardware device.
StatusByte
Specifies a status byte that is read from I/O port 60 when the interrupt occurs.
DataByte
Specifies a data byte that is read from I/O port 64 when the interrupt occurs.
ContinueProcessing
Specifies, if TRUE, that the I8042prt mouse ISR continues processing after this callback returns. Otherwise, processing is not continued.
MouseState
Pointer to a MOUSE_STATE enumeration value, which identifies the state of mouse input.
ResetSubState
Pointer to MOUSE_RESET_SUBSTATE enumeration value, which identifies the mouse reset substate. See the Remarks section.
Return value
MouFilter_IsrHook returns TRUE if the interrupt service routine should continue; otherwise it returns FALSE.
*/
BOOLEAN MouFilter_IsrHook(
PDEVICE_OBJECT DeviceObject,
PMOUSE_INPUT_DATA CurrentInput,
POUTPUT_PACKET CurrentOutput,
UCHAR StatusByte,
PUCHAR DataByte,
PBOOLEAN ContinueProcessing,
PMOUSE_STATE MouseState,
PMOUSE_RESET_SUBSTATE ResetSubState
);
如果 I8042prt 的默认操作足够,则不需要 MouFilter_IsrHook 回调。
I8042prt 鼠标 ISR 在验证中断后调用 MouFilter_IsrHook 。
为了重置鼠标,I8042prt 会经历一系列操作子状态。 MOUSE_RESET_SUBSTATE枚举值标识每个子状态。 有关 I8042prt 如何重置鼠标和相应的鼠标重置子状态的详细信息,请参阅 ntdd8042.h 中MOUSE_RESET_SUBSTATE的文档。
MouFilter_IsrHook 在 I8042prt 鼠标 ISR 的 IRQL 的内核模式下运行。
MouFilter_ServiceCallback
请参阅 PSERVICE_CALLBACK_ROUTINE
/*
Parameters
DeviceObject [in]
Pointer to the class device object.
InputDataStart [in]
Pointer to the first mouse input data packet in the input data buffer of the port device.
InputDataEnd [in]
Pointer to the mouse input data packet immediately following the last data packet in the port device's input data buffer.
InputDataConsumed [in, out]
Pointer to the number of mouse input data packets that are transferred by the routine.
Return value
None
*/
VOID MouFilter_ServiceCallback(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PMOUSE_INPUT_DATA InputDataStart,
_In_ PMOUSE_INPUT_DATA InputDataEnd,
_Inout_ PULONG InputDataConsumed
);
I8042prt 的 ISR DPC 调用 MouFilter_ServiceCallback,然后调用 MouseClassServiceCallback。 可以将筛选器服务回调配置为修改从设备的输入缓冲区传输到类数据队列的输入数据。 例如,回调可以删除、转换或插入数据。
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈