ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

DsHidMini 力反馈(FFB)实现全解析:从 PID 报告描述符到 Windows DirectInput 效果注册

DsHidMini 力反馈(FFB)实现全解析:从 PID 报告描述符到 Windows DirectInput 效果注册 驱动开发硬件开发【免费下载链接】DsHidMiniVirtual HID Mini-user-mode-driver for Sony DualShock 3 Controllers项目地址https://gitcode.com/gh_mirrors/ds/DsHidMini点击查看免费下载本技术指南以仓库 docs/FFB_NOTES.md 为主线完整剖析 DsHidMini 如何通过 HID 物理接口设备PIDPhysical Interface Device协议把 Sony DualShock 3 手柄的振动电机暴露为 Windows DirectInput 力反馈设备。读者将掌握 PID 报告描述符的组织方式、驱动侧效果块分配与马达映射原理、Windows 注册表中效果属性的二进制编码格式以及如何用 WPP 跟踪日志与注册表删除操作排障。1. 概览DS3 震动如何变成力反馈DsHidMini 是一个面向 Sony DualShock 3 控制器的虚拟 HID 用户态迷你驱动。普通模式下 DS3 的左右马达强度通过输出报告字节直接驱动而在 FFB 模式下驱动在 HID 报告描述符中追加了完整的 PID 集合PID PageUsage Page 0x0F使 Windows 将设备识别为标准的力反馈摇杆DirectInput 应用程序如游戏中的震动设置通过IDirectInputEffect接口下发 Constant Force、Sine、Square 等效果由驱动翻译成 DS3 马达强度后经输出报告送入手柄。整条链路如下DirectInput 应用程序 │ IDirectInputEffect 调用 ▼ Windows dinput 设备栈读取注册表效果属性 │ HID PID 输出/功能报告 ▼ DsHidMini 驱动HID.Reports.c / HID.FeatureReport.c │ 映射为 DS3 左右马达强度0-255 ▼ DS3 输出报告OutputReport.c → Ds3_GetRawOutputReportBuffer ▼ USB / 蓝牙 传输到手柄FFB 功能由编译期宏DSHM_FEATURE_FFB控制在 driver/Driver.h 中默认定义开启。2. PID 报告描述符的组织方式PID 是一组模拟效果的 HID 定义设备通过它向 Windows 声明自己能播放多少种效果。DsHidMini 采用片段式组织每种报告Set Effect、Effect Operation、PID Pool 等是一个独立的头文件由 driver/DsHid.c 按固定顺序包含进报告描述符。所有三种 HID 模式Split、Single、CGP都会在游戏手柄集合之后追加同一组 PID 片段#ifdef DSHM_FEATURE_FFB #include PID/01_PIDStateReport.h #include PID/02_SetEffectReport.h #include PID/03_SetEnvelopeReport.h #include PID/04_SetConditionReport.h #include PID/05_SetPeriodicReport.h #include PID/06_SetConstantForceReport.h #include PID/07_SetRampForceReport.h #include PID/08_CustomForceDataReport.h #include PID/09_DownloadForceSample.h #include PID/10_EffectOperationReport.h #include PID/11_PIDBlockFreeReport.h #include PID/12_PIDDeviceControl.h #include PID/13_DeviceGainReport.h #include PID/14_SetCustomForceReport.h #include PID/15_CreateNewEffectReport.h #include PID/16_PIDBlockLoadReport.h #include PID/17_PIDPoolReport.h #endif文件名前的数字仅用于指定包含顺序与报告 ID 无关见 driver/PID/README.md。2.1 报告 ID 布局所有报告 ID 常量集中在 driver/PID/PIDTypes.h报告 ID常量类型0x04PID_INPUT_REPORT_ID输入PID 状态0x10PID_SET_EFFECT_REPORT_ID输出0x11PID_SET_ENVELOPE_REPORT_ID输出0x12PID_SET_CONDITION_REPORT_ID输出0x13PID_SET_PERIODIC_REPORT_ID输出0x14PID_SET_CONSTANT_FORCE_REPORT_ID输出0x15PID_SET_RAMP_FORCE_REPORT_ID输出0x16PID_SET_CUSTOM_FORCE_DATA_REPORT_ID输出0x17PID_DOWNLOAD_SAMPLE_REPORT_ID输出0x18PID_EFFECT_OPERATION_REPORT_ID输出0x19PID_DEVICE_CONTROL_REPORT_ID输出0x1APID_BLOCK_FREE_REPORT_ID输出0x1BPID_DEVICE_GAIN_REPORT_ID输出0x1CPID_SET_CUSTOM_FORCE_REPORT_ID输出0x20PID_NEW_EFFECT_REPORT_ID功能0x21PID_BLOCK_LOAD_REPORT_ID功能0x22PID_POOL_REPORT_ID功能示例片段 driver/PID/02_SetEffectReport.h 展示了 PID 页中逻辑/物理最小最大值等约束的写法Duration/Sample Period 等时间量程 032767单位 0x10, 秒 ×10⁻³Gain 与设备增益 010000Trigger Button 1128方向 0359990.01° 精度。2.2 效果类型与块管理PIDTypes.h 定义了 PID 规范要求的两个关键枚举直接与文档中的跟踪日志对应PID_EFFECT_TYPEPIDTypes.hConstant Force1、Ramp2、Square3、Sine4、Triangle5、Sawtooth Up6、Sawtooth Down7、Spring8、Damper9、Inertia10、Friction11外加描述符中的 Custom Force12PID_DEVICE_CONTROLPIDTypes.hPidDcEnableActuators1、PidDcDisableActuators2、PidDcStopAllEffects3、PidDcReset4、PidDcPause5、PidDcContinue6——与 FFB_NOTES 文档中的DeviceControlEnum完全一致。驱动维护一个效果块池容量MAX_EFFECT_BLOCKS 127PIDTypes.h。每个效果块用FFB_ATTRIBUTES结构跟踪状态EffectBlockIndex、EffectType、IsReserved、IsReported底层以 DMF HashTable 模块DmfModuleForceFeedback存储供报告处理函数读写。3. Windows 端效果注册注册表 OEMMediaProperties 格式要让 DirectInput 识别设备的力反馈能力除了 HID 描述符外还需要在注册表写入效果属性DIEFFECTATTRIBUTES路径为HKEY_CURRENT_USER\System\CurrentControlSet\Control\MediaProperties\PrivateProperties\Joystick\OEM\VID_054CPID_0268\OEMForceFeedback\Effects\GUID\AttributesDsHidMini 文档指出该二进制值即DIEFFECTATTRIBUTES结构。效果 GUID 由厂商自行分配微软在 WinXP 自带 INF 中预置了一套本仓库文档完整摘录了 FFB_NOTES.md 中从{13541C20-...}常量力到{e84cd1c8-...}RTC Spring 的 40 余条效果条目。3.1 DIEFFECTATTRIBUTES 二进制解码以文档摘录的 SideWinder FFB2 INF 为例HKLM,....\Effects\{13541C20-8E33-11D0-9AD0-00A0C9A06E35},Attributes,0x00000001, 65,00,02,00,01,00,00,00,ed,01,00,00,cd,01,00,00,30,00,00,00解码规则DWORD 小端序偏移字节含义0x0065,00dwEffectType 0x0065LOBYTE(0x65)0x01→DIEFT_CONSTANTFORCE0x0402,00dwStaticParams 0x0002含DIEP_SAMPLEPERIOD0x0801,00,00,00dwDynamicParams 0x00000001含DIEP_DURATION0x0Ced,01,00,00dwCoarseDuration 0x01ED 493微秒0x10cd,01,00,00dwFineDuration 0x01CD 461微秒0x1430,00,00,00dwGain 0x30 483.2 效果类型与参数标志位FFB_NOTES 文档给出了 DirectInput 头文件中这两组位标志的完整定义解析时可直接对照效果类型DIEFT_*——DIEFT_GETTYPE(n)取低字节DIEFT_ALL 0x00000000、DIEFT_CONSTANTFORCE 0x00000001、DIEFT_RAMPFORCE 0x00000002、DIEFT_PERIODIC 0x00000003、DIEFT_CONDITION 0x00000004、DIEFT_CUSTOMFORCE 0x00000005、DIEFT_HARDWARE 0x000000FF能力修饰位DIEFT_FFATTACK 0x00000200起振、DIEFT_FFFADE 0x00000400衰减、DIEFT_SATURATION 0x00000800饱和、DIEFT_POSNEGCOEFFICIENTS 0x00001000、DIEFT_POSNEGSATURATION 0x00002000、DIEFT_DEADBAND 0x00004000、DIEFT_STARTDELAY 0x00008000。静态参数DIEP_*DIEP_DURATION 0x00000001、DIEP_SAMPLEPERIOD 0x00000002、DIEP_GAIN 0x00000004、DIEP_TRIGGERBUTTON 0x00000008、DIEP_TRIGGERREPEATINTERVAL 0x00000010、DIEP_AXES 0x00000020、DIEP_DIRECTION 0x00000040、DIEP_ENVELOPE 0x00000080、DIEP_TYPESPECIFICPARAMS 0x00000100DirectInput 6.0 以上新增DIEP_STARTDELAY 0x00000200因此DIEP_ALLPARAMS_DX5 0x000001FF而DIEP_ALLPARAMS 0x000003FFDirectInput 6.0 以下仍为 0x000001FF运行时标志DIEP_START 0x20000000、DIEP_NORESTART 0x40000000、DIEP_NODOWNLOAD 0x80000000。3.3 仓库中的注册表范例仓库另附一份可直接导入的完整注册表示例 docs/FFB_REG_EXAMPLE.reg以罗技 WingMan Strike Force 3D USBVID_046DPID_C285为例展示三种层的写法设备级ConfigCLSID{60150956-C4AE-11D1-B59B-00A0C9971EFC}、OEMName、OEMDataOEMForceFeedback 级Attributes 00,00,00,00,e8,03,00,00,e8,03,00,00Gain 上限 10000、CLSID {8D533A43-7A5F-11D3-8297-0050DA1A72D3}Effects 级每个效果一个GUID键Attributes编码效果类型与参数默认值为效果名称Constant、Sine Wave、Spring 等。注意该文件中的效果 GUID 与文档中微软预置 GUID 完全一致即各厂商可直接复用同一组 GUID。3.4 排障清除过期的 MediaProperties 缓存FFB_NOTES 文档强调当修改了设备属性或效果定义后 Windows 仍显示旧值直接删除整个缓存键再重插设备即可强制重建Computer\HKEY_CURRENT_USER\System\CurrentControlSet\Control\MediaProperties这一操作等价于删除上文所有 OEM 效果子键Windows 会依据驱动报告描述符重新枚举并生成默认效果属性。4. 驱动侧效果块生命周期SetFeature/GetFeature 与 HashTable4.1 池查询PID_POOL_REPORT0x22驱动收到PID_POOL_REPORT_ID请求后driver/HID.FeatureReport.c返回虚拟内存池的静态信息pPool-RamPoolSize 65535; pPool-SimultaneousEffectsMax MAX_EFFECT_BLOCKS; // 127 pPool-DeviceManagedPool 1; pPool-SharedParameterBlocks 0;由于一切效果都在软件层模拟池大小直接报满值。FFB_NOTES 文档中的跟踪日志DsHidMini_GetFeature ... !! PID_POOL_REPORT_ID正是此分支的 WPP 输出。4.2 创建新效果PID_NEW_EFFECT_REPORT0x20DSHM_SetFeaturedriver/HID.FeatureReport.c收到PID_NEW_EFFECT_REPORT_ID后在 1126 范围内线性扫描哈希表找空闲效果块将EffectType存入并置IsReserved TRUE、IsReported FALSE。找不到空闲块时调用EventWriteFFBNoFreeEffectBlockIndex()并返回STATUS_INVALID_PARAMETER。随后按效果类型打印跟踪!! ET Constant Force、!! ET Square……与文档的 Constant Force 测试日志完全对应。4.3 认领效果块PID_BLOCK_LOAD_REPORT0x21DSHM_GetFeaturedriver/HID.FeatureReport.c收到PID_BLOCK_LOAD_REPORT_ID后遍历哈希表找到已保留但未认领IsReserved !IsReported的条目把该块索引写入响应并置IsReported TRUE。若全部块都已被认领则返回PidBlsFull。4.4 释放效果块PID_BLOCK_FREE_REPORT0x1APID_BLOCK_FREE_REPORT_ID处理driver/HID.Reports.c将对应索引的条目重置为IsReserved FALSE; IsReported FALSE并写回哈希表完成用完即还。5. 效果参数到 DS3 马达强度的映射5.1 Set Effect / Device Control / Gain 报告DSHM_WriteReport内嵌的 FFB 分支driver/HID.Reports.c逐一解析输出报告PID_DEVICE_CONTROL_REPORT_ID0x19按PID_DEVICE_CONTROL枚举执行。Reset会遍历哈希表清除所有块标记并fall through到StopAllEffectsStopAllEffects调用DS3_SET_BOTH_RUMBLE_STRENGTH(0x00, 0x00)并触发一次输出报告发送Ds3OutputReportSourceForceFeedback对应文档示例中的!! DC ResetPID_DEVICE_GAIN_REPORT_ID0x1B读取并打印DeviceGain如 10000用于全局增益缩放PID_SET_EFFECT_REPORT_ID0x10记录 13 个字段——EffectBlockIndex、EffectType、Duration、TriggerRepeatInterval、SamplePeriod、Gain、TriggerButton、AxesEnableX/Y、DirectionEnable、DirectionInstance1/2、StartDelayPID_SET_PERIODIC_REPORT_ID0x13记录Magnitude、Offset、Phase、Period。Rumble Racing 示例中每个效果周期内Period恒为 2000、Magnitude为 0而Offset不断变化0 → 7254 → 4549 → …说明该游戏用周期波的直流偏置实现低频脉冲式震动驱动侧只需照单记录即可DS3 只有两个马达无法真正输出周期波形细节映射在 Constant Force 层完成PID_SET_CONDITION_REPORT_ID0x12解析CpOffset、正/负系数、正/负饱和、DeadBand等条件效果参数Spring/Damper/Inertia/Friction 共用。5.2 Constant Force → 左右马达核心映射PID_SET_CONSTANT_FORCE_REPORT_ID0x14是真正驱动马达的报告driver/HID.Reports.crumbleValue (UCHAR)(pSetConstant-Magnitude / 10000.0f * 255.0f); if (pSetConstant-EffectBlockIndex % 2 0) { DS3_SET_SMALL_RUMBLE_STRENGTH(DeviceContext, rumbleValue); } else { DS3_SET_LARGE_RUMBLE_STRENGTH(DeviceContext, rumbleValue); }实现要点PID 的力值范围是-1000010000DS3 马达强度是0255这里按Magnitude / 10000 × 255线性换算负值会被截断为 0效果块索引的奇偶性决定马达偶数索引 → 小马达Small/Large奇数索引 → 大马达Large。这是把双马达手柄桥接到多效果块 PID 池的关键约定DirectInput 客户端可以通过创建两个 Constant Force 效果占用两个块分别控制两个马达文档中的 Constant Force 测试日志与之一一对应PID_CREATE_NEW_EFFECT_REPORT块分配→ET Constant Force→PID_BLOCK_LOAD_REPORT_ID块 1 认领→PID_SET_CONSTANT_FORCE_REPORT, Magnitude: 0初始 0→SET_EFFECT_REPORT, EffectBlockIndex: 1→Magnitude: 10000满强度→ 三次PID_EFFECT_OPERATION_REPORT播放/更新→PID_BLOCK_FREE_REPORT释放→DC Reset。5.3 效果播放控制PID_EFFECT_OPERATION_REPORT0x18PidEoStartStart1触发一次DSHM_SendOutputReport把当前累积的马达强度送往设备PidEoStopStop3先清零两马达再发送。StartSolo2目前仅记录。这解释了日志中每次PID_EFFECT_OPERATION_REPORT, EffectOperation: 1后设备震动更新一次的现象。5.4 输出报告的最终发送马达强度写入 DS3 输出报告缓冲区后由 driver/OutputReport.c 统一交付USB 优先走中断 OUT可配置为控制端点蓝牙走IOCTL_BTHPS3_HID_CONTROL_WRITE或IOCTL_BTHPS3_HID_INTERRUPT_WRITEBluetoothOutputReportTransport配置项见 OutputReport.c蓝牙链路还内置了按OutputRateControlPeriodMs的发送限速与缓冲替换逻辑避免 FFB 高频更新打爆蓝牙通道。FFB 来源统一标记为Ds3OutputReportSourceForceFeedback与驱动自身 LED 写入、HID 应用写入互不冲突LED 自定义模式会在发送边界重新应用见 OutputReport.c。6. WPP 跟踪日志解读与调试6.1 Sniff 日志的字段含义FFB_NOTES 文档的 Sniffs/Logs 一节记录了若干 GetFeature/WriteReport/SetFeature 调用。结合源码可解读出reportId为 PID 报告 ID如 7、12、13、5、6 等视具体配置而定可能与上述标准 ID 不同取决于客户栈实际下发的调用ReportSize: 32760表示 GetFeature 的接收缓冲远大于实际报告reportBufferLen为实际长度。状态码STATUS_SUCCESS (0x00000000)表示成功STATUS_NO_SUCH_DEVICE (0xC000000E)表示请求了未实现的报告——文档Example traces中reportId: 19的 GetFeature 返回该错误正是因为 0x19 是输出类报告 IDDevice Control不支持 GetFeature。6.2 三条典型链路日志开关震动ON/OFFWriteReport reportId 28/29效果播放触发→SetFeature reportId 17周期/常量参数更新→GetFeature reportId 18读取效果状态id:1。Constant Force 测试见 5.2 节的完整块生命周期是文档中逐行列举、可在 driver/HID.FeatureReport.c 与 driver/HID.Reports.c 中逐一找到对应分支的标准序列。Rumble Racing 示例两个效果块Square Sine并行大量PID_SET_PERIODIC_REPORT, EffectBlockIndex: 1, Magnitude: 0, Offset: 值, Phase: 0, Period: 2000之后跟PID_EFFECT_OPERATION_REPORT, EffectBlockIndex: 1, EffectOperation: 1, LoopCount: 1构成每帧更新偏置→播放的循环结束时释放块 1、块 2 保持满偏置输出最终DC Reset。该日志验证了驱动对SET_PERIODIC的实时转发与EFFECT_OPERATION的发送触发行为。7. 相关参考资料FFB_NOTES 文档列出的研究来源raphnet/gc_n64_usb、adapt-ffb-joy、ArduinoJoystickWithFFBLibrary、vJoy、ShieldControllerWinDriver、Microchip 论坛 PIC18F4550 教程等共同构成了本仓库 FFB 描述符的参考基线仓库内可直接深挖的实现材料还包括PID 类型与常量driver/PID/PIDTypes.hPID 报告描述符片段driver/PID/0117 号头文件功能报告处理池/块分配driver/HID.FeatureReport.c输出报告处理与马达映射driver/HID.Reports.c输出报告发送管线driver/OutputReport.c注册表效果属性范例docs/FFB_REG_EXAMPLE.reg调试工具debugging/dshidmini_debug.reg、debugging/enable_etw.ps18. 常见问题设备被识别为普通手柄而非力反馈设备确认驱动构建时DSHM_FEATURE_FFB已定义且注册表OEMForceFeedback\Effects下有效果 GUID 条目必要时删除整个MediaProperties键见 3.4后重插设备。效果属性解析错误对照 3.1 节的字段表逐 DWORD 校验Attributes的字节序并确认dwEffectType低字节落入 3.2 节的类型范围。单个效果块只能驱动一个马达按 5.2 节约定偶数/奇数块分别映射小/大马达需用两个效果分别控制两个马达。蓝牙下震动延迟/丢更新检查OutputRateControlPeriodMs与BluetoothOutputReportTransport配置见 5.4FFB 高频更新会被限速与缓冲合并策略收敛。赞分享驱动开发硬件开发【免费下载链接】DsHidMiniVirtual HID Mini-user-mode-driver for Sony DualShock 3 Controllers项目地址https://gitcode.com/gh_mirrors/ds/DsHidMini点击查看免费下载相关推荐PCSX2模拟器力反馈(FFB)支持的技术演进与优化PCSX2模拟器力反馈 FFB 支持的技术演进与优化 力反馈技术在游戏模拟中的重要性 力反馈 Force Feedback, FFB 是现代游戏外设中提升沉浸感虚拟化桌面应用图形学TinyUSB UAC2 异步反馈 USB 音箱双速率描述符与 FIFO 计数反馈机制的完整实现解析TinyUSB UAC2 异步反馈 USB 音箱双速率描述符与 FIFO 计数反馈机制的完整实现解析 本篇文章以 uac2_speaker_fb 示例 htt嵌入式驱动开发通信物联网告别无效反馈magnetW用户反馈系统全攻略从实现到管理告别无效反馈magnetW用户反馈系统全攻略从实现到管理 你是否遇到过用户反馈石沉大海的尴尬是否在调试用户问题时缺乏有效数据支持magnetW用户反馈系桌面应用网页爬虫上一篇Mythos-nano vs 主流大模型为什么小参数模型能在数学竞赛中脱颖而出下一篇ScalaCheck快速入门如何在5分钟内编写你的第一个属性测试创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进