键盘和鼠标 HID 客户端驱动程序

注意

本主题适用于为键盘和鼠标 HID 客户端创建驱动程序的开发人员。 如果要修复鼠标或键盘,请参阅:

本主题讨论键盘和鼠标 HID 客户端驱动程序。 键盘和鼠标表示在 HID 使用情况表中标准化的第一组 HID 客户端,并在Windows操作系统中实现。

键盘和鼠标 HID 客户端驱动程序以 HID 映射器驱动程序的形式实现。 HID 映射器驱动程序是内核模式 WDM 筛选器驱动程序,为非 HID 类驱动程序和 HID 类驱动程序之间的 I/O 请求提供双向接口。 映射器驱动程序将一个的 I/O 请求和数据协议映射到另一个请求。

Windows为 HID 键盘和 HID 鼠标设备提供系统提供的 HID 映射器驱动程序。

体系结构和概述

下图演示了 USB 键盘、鼠标和触摸板设备的系统提供的驱动程序堆栈。

keyboard and mouse driver stack diagram, showing the hid class mapper drivers for keyboards and mice, along with the keyboard and mouse class drivers.

上图包含以下组件:

  • KBDHID.sys:键盘的 HID 客户端映射器驱动程序。 将 HID 用法转换为扫描码,以与现有键盘类驱动程序进行交互。
  • MOUHID.sys:用于鼠标/触摸板的 HID 客户端映射器驱动程序。 将 HID 用法转换为鼠标命令, (X/Y、按钮、滚轮) 以与现有键盘类驱动程序进行交互。
  • KBDCLASS.sys键盘类驱动程序 以安全的方式维护系统上所有键盘和键盘的功能。
  • MOUCLASS.sys鼠标类驱动程序 维护系统上所有鼠标/触摸板的功能。 驱动程序支持绝对和相对指向设备。 这不是触摸屏的驱动程序,因为Windows中由其他驱动程序管理。
  • HIDCLASS.sysHID 类驱动程序。 HID 类驱动程序是KBDHID.sys与MOUHID.sys HID 客户端和各种传输之间的粘附, (USB、蓝牙等) 。

系统生成驱动程序堆栈,如下所示:

  • 传输堆栈为每个附加的 HID 设备创建物理设备对象 (PDO) ,并加载相应的 HID 传输驱动程序,后者又加载 HID 类驱动程序。
  • HID 类驱动程序为每个键盘或鼠标 TLC 创建 PDO。 复杂的 HID 设备 (超过 1 个 TLC) 公开为 HID 类驱动程序创建的多个 PDO。 例如,具有集成鼠标的键盘可能具有一个标准键盘控件的集合,以及鼠标的不同集合。
  • 键盘或鼠标隐藏客户端映射器驱动程序加载到相应的 FDO 上。
  • HID 映射器驱动程序为键盘和鼠标创建 FDO,并加载类驱动程序。

重要说明:

  • 符合支持的 HID 用法和顶级集合的键盘和鼠标不需要供应商驱动程序。
  • 供应商可以选择在 HID 堆栈中提供筛选器驱动程序,以更改/增强这些特定 TLC 的功能。
  • 供应商应创建特定于供应商的单独 TLC,以在其隐藏客户端和设备之间交换供应商专有数据。 除非关键,否则请避免使用筛选器驱动程序。
  • 系统将打开所有键盘和鼠标集合供其独占使用。
  • 系统阻止禁用/启用键盘。
  • 该系统支持具有平滑滚动功能的水平/垂直方向盘。

驱动程序指南

Microsoft 为编写驱动程序的 IHV 提供了以下指南:

  1. 允许驱动程序开发人员以筛选器驱动程序或新的 HID 客户端驱动程序的形式添加其他驱动程序。 下面描述了这些条件:

    1. 筛选器驱动程序:驱动程序开发人员应确保其增值驱动程序是筛选器驱动程序,并且不会替换 (,也不会代替输入堆栈中现有Windows HID 驱动程序) 。

      • 在以下方案中允许筛选器驱动程序:
        • 作为 kbdhid/mouhid 的上层筛选器
        • 作为 kbdclass/mouclass 的上层筛选器
      • 不建议将筛选器驱动程序用作 HIDCLASS 和 HID 传输微型驱动程序之间的筛选器
    2. 函数驱动程序:或者供应商可以创建函数驱动程序 (而不是筛选器驱动程序) ,但仅针对供应商特定的 HID PDO (用户模式服务(如有必要)) 。

      以下情况下允许函数驱动程序:

      • 仅加载特定供应商的硬件
    3. 传输驱动程序:Windows团队不建议创建额外的 HID 传输微型驱动程序,因为它们是复杂的驱动程序来写入/维护。 如果合作伙伴正在创建新的 HID 传输微型驱动程序,尤其是在 SoC 系统上,我们建议进行详细的体系结构评审,以了解推理并确保正确开发驱动程序。

  2. 驱动程序开发人员应利用驱动程序框架 (KMDF 或 UMDF) ,而不依赖 WDM 来获取其筛选器驱动程序。

  3. 驱动程序开发人员应减少其服务与驱动程序堆栈之间的内核用户转换数。

  4. 驱动程序开发人员应确保能够通过键盘和触摸板功能唤醒系统 (最终用户 (设备管理器) 或电脑制造商) 调整。 除了在 SoC 系统上,这些设备必须能够在系统处于工作 S0 状态时从低功率状态唤醒自己。

  5. 驱动程序开发人员应确保其硬件高效管理。

    • 当设备处于空闲状态时,设备可以进入其最低电源状态。
    • 当系统处于低功率状态 (时,设备处于最低电源状态,例如备用 (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用于激活新的 45& 按钮 + 滚轮模式的方法是用于激活 IntelliMouse 兼容鼠标中的第三个按钮和滚轮的方法的扩展:

  • 首先,鼠标设置为 3 按钮滚轮模式,该模式是通过将报表速率连续设置为 200 个报表/秒、100 个报告/秒、80 个报表/秒来完成的,然后从鼠标读取 ID。 完成此序列时,鼠标应报告 ID 为 3。
  • 接下来,鼠标设置为 5 按钮滚轮模式,通过连续将报表速率设置为 200 个报表/秒,然后再次设置为 200 个报表/秒,再读取 80 个报表/秒,然后从鼠标读取 ID 来完成。 完成此序列后,5 按钮滚轮鼠标应报告 ID 为 4 (,而 IntelliMouse 兼容的 3 按钮滚轮鼠标仍会报告 ID 为 3) 。

请注意,这仅适用于 PS/2 小鼠,不适用于 HID 小鼠 (HID 小鼠必须在报表描述符) 中报告准确的使用情况。

标准 PS/2 兼容的鼠标数据包格式 (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 是3 是2 是1 Y0 Y 数据字节

注意

Windows鼠标驱动程序不会检查溢出位。 如果溢出,鼠标应只发送最大有符号位移值。

标准 PS/2 兼容的鼠标数据包格式 (3 个按钮 + VerticalWheel)

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 是3 是2 是1 Y0 Y 数据字节
4 Z7 Z6 Z5 Z4 Z3 Z2 Z1 Z0 Z/wheel 数据字节

标准 PS/2 兼容的鼠标数据包格式 (5 个按钮 + VerticalWheel)

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 是3 是2 是1 Y0 Y 数据字节
4 0 0 B5 B4 Z3 Z2 Z1 Z0 Z/wheel 数据和按钮 4 和 5

重要

请注意,5 按钮滚轮鼠标的 Z/wheel 数据已减少到 4 位,而不是 IntelliMouse 兼容 3 按钮滚轮模式中使用的 8 位。 由于轮轮在给定中断期间通常无法生成超出范围 +7/-8 的值,因此可以实现这种缩减。 Windows鼠标驱动程序将在鼠标处于 5 按钮滚轮模式时对四个 Z/wheel 数据位进行签名,当鼠标在 3 按钮滚轮模式下操作时,将满 Z/滚轮数据字节进行签名。

按钮 4 & 5 映射到WM_APPCOMMAND消息,对应于App_Back和App_Forward。

不需要供应商驱动程序的设备

以下设备不需要供应商驱动程序:

  • 符合 HID 标准的设备。
  • 由系统提供的非 HIDClass 驱动程序运行的键盘、鼠标或游戏端口设备。

Kbfiltr 示例

Kbfiltr 设计用于 Kbdclass、键盘设备的系统类驱动程序和 I8042prt(PS/2 样式键盘的函数驱动程序)。 Kbfiltr 演示如何筛选 I/O 请求以及如何添加修改 Kbdclass 和 I8042prt 操作的回调例程。

有关 Kbfiltr 操作的详细信息,请参阅以下内容:

Kbfiltr IOCTLs

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 实现的回调例程

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 及更高版本Windows的 PS/2 样式鼠标的函数驱动程序。 Moufiltr 演示如何筛选 I/O 请求并添加修改 Mouclass 和 I8042prt 操作的回调例程。

有关 Moufiltr 操作的详细信息,请参阅以下内容:

Moufiltr 控制代码

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 回调可以使用的回调的指针。

Moufiltr 回调例程

IOCTL_INTERNAL_MOUSE_CONNECT

IOCTL_INTERNAL_MOUSE_CONNECT请求将 Mouclass 服务连接到鼠标设备。

IOCTL_INTERNAL_MOUSE_DISCONNECT

IOCTL_INTERNAL_MOUSE_DISCONNECT请求由 Moufiltr 完成,错误状态为 STATUS_NOT_IMPLEMENTED。

对于所有其他请求,Moufiltr 会跳过当前的 IRP 堆栈,并将请求发送到设备堆栈,而无需进一步处理。

回调例程

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。 可以将筛选器服务回调配置为修改从设备的输入缓冲区传输到类数据队列的输入数据。 例如,回调可以删除、转换或插入数据。