0

我正在尝试通过 Microsoft HID API 与 XBOX ONE 控制器交谈,而不使用XINPUT。我目前能够通过使用HidD_SetOutputReport(HANDLE, VOID*, ULONG). 但是我一直坚持使用HidD_GetInputReport(HANDLE, VOID*, ULONG)或使用和不使用和使用Windows 事件ReadFile() / ReadFileEx()创建 HANDLE 来读取按钮值。FILE_FLAG_OVERLAPPEDOVERLAPPED

在以下文章https://github.com/quantus/xbox-one-controller-protocol的帮助下,我已经对 USB URB 协议进行了逆向工程。主要目标是克服 XINPUT 开销并编写一个灵活的框架,以便我也可以集成其他游戏手柄。

这就是我完成的:

  1. 我已经通过 USB 将游戏手柄连接到我的电脑(这样我就可以读取从设备发送和接收的所有 USB 包)
  2. 我使用SetupDiGetClassDevs(...)SetupDiEnumDeviceInfo(...)和找到了控制器SetupDiEnumDeviceInterfaces(...)的路径SetupDiGetDeviceInterfaceDetail(...)
  3. 我已经使用创建了设备的句柄HANDLE gamePad = CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL)
  4. UsingHidP_GetCaps(HANDLE, HIDP_CAPS*)似乎没有返回有效数据,因为它报告的 OutputReportByteLength 为 0,但我能够发送大小为 5(打开)和 9(设置震动马达)的输出报告
  5. 所有输入和输出数据(至少按钮和 Rumble 电机)似乎都遵循以下模式
byte 0: Package type
byte 1: was always 0x00
byte 2: Package number (always incrementing every package)
byte 3: Size of following data
byte 4+: <Data>
  1. 有了这些信息,我就可以让马达和触发器按照我的意愿发出隆隆声

例如:我的两个输出隆隆声数据包看起来像这样(使用脉冲长度来脏地打开和关闭电机): Device Monitoring Studio URB 视图中的输出包 这会打开所有电机,其中隆隆声电机的速度为 0xFF,触发器的速度为 0xF0。我是这样做的:

struct RumbleContinous{
    BYTE mode;
    BYTE mask; // Motor Mask 0b 0 0 0 0 LT RT L R
    BYTE lTForce;
    BYTE rTForce;
    BYTE lForce;
    BYTE rForce;
    BYTE pulseLength;
    BYTE offTime;
    BYTE terminator; // Terminator / Dummy / ?? (XINPUT sends that as 0xEB!) / Changing seems to not make any changes
};

RumbleContinous rc = {0x00, 0x0F, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF 0x00, 0xEB};

HidD_SetOutputReport(gamePad, (PVOID)&rc, sizeof(RumbleContinous ));

现在我的问题

查看来自控制器的输入包,您似乎需要创建一个大小为 0x0E = 14 的缓冲区,ZeroMemory 它(或者像 MSDN 建议的那样将第一个字节写入 0)然后调用HidD_GetInputReport(HANDLE, buffer, 14) Device Monitoring Studio URB 视图中的输入包

所以我所做的是调用HidD_FlushQueue()以确保下一个包是输入包。然后我插入一点延迟,以便能够更改一些控制器值。之后我尝试读入一个 BYTE 数组,HidD_GetInputReport(HANDLE, cmd_in, 14)但函数总是失败GetLastError() == 0x00000057 // ERROR_INVALID_PARAMETER

由于 HID 能够过滤包,因此可能需要分配一个比预期大一字节的缓冲区,并将所需的报告 ID 传递到位置 0 的缓冲区。这就是我所做的:

BYTE cmd_in[15];
ZeroMemory(cmd_in, 15);
cmd_in[0] = 0x20;
HidD_GetInputReport(gamePad, cmd_in, 15);

仍然没有成功。由于该HidP_GetCaps(...)函数报告了 16 的输入报告(但我不相信这一点,因为它已经用 0 的输出报告大小欺骗了我)我尝试扫描许多缓冲区大小:

BYTE cmd_in[30];
for (UINT bs = 0; bs < 30; bs++) {
    ZeroMemory(cmd_in, 30);

    HidD_FlushQueue(gamePad); // Flushing works
    Sleep(500);
        
    if (HidD_GetInputReport(gamePad, cmd_in, bs)) {
        // Output result (ommited)
    }
    else {
        // Print error (ommited)
    }
}

BYTE cmd_in[30];
for (UINT bs = 0; bs < 30; bs++) {
    ZeroMemory(cmd_in, 30);
    cmd_in[0] = 0x20;

    HidD_FlushQueue(gamePad); // Flushing works
    Sleep(500);
        
    if (HidD_GetInputReport(gamePad, cmd_in, bs)) {
        // Output result (ommited)
    }
    else {
        // Print error (ommited)
    }
}

仍然没有成功。根据特殊要求的输出格式和错误的HidP_GetCaps(...)读数,我怀疑 XBOX ONE 游戏手柄驱动程序需要输入缓冲区中已经存在的特殊标头(据我所知 HidD_GetInputReport(...) 只是调用用户/内核模式驱动程序调用返回;因此驱动程序可以自由地执行检查并拒绝发送给它的所有数据)

也许那里的任何人都知道如何为 XBOX One 控制器调用 HidD_GetInputReport(...)

我知道可以检索输入数据,因为SimpleHIDWrite能够看到按钮状态。即使格式完全不同(例如,两个触发器组合在一个字节中。在 USB Packed 中,每个触发器都有自己的字节):

SimpleHIDWrite Xbox ONE 控制器

我还应该提到,HIDWrite 无需按任何按钮即可查看数据!查看来自 SimpleHIDWrite 的日志,它看起来像是RD0015 个字节的数据中读取的,有一个 16 字节的数组和 00 处的元素 0(在我的应用程序中不起作用)。或者它只是转储所有传入的数据。如果是,这怎么可能?这对我来说也是一个选择!

4

1 回答 1

0

我查看了执行以下代码时 XINPUT 正在做什么:

XINPUT_STATE s;
XInputGetState(0, &s);

事实证明,在从控制器读取数据之前,XINPUT 正在做同样的事情。XINPUT 的HidD_GetInputReport(...)Insteal 正在调用DeviceIoControl(...)。所以我所做的是一个快速的谷歌搜索“DeviceIoControl xbox”和tada,它不需要自己弄清楚内存布局:Getting xbox controller input without xinput

编辑:DeviceIoControl(...)即使游戏手柄通过蓝牙连接也可以使用,而当HidD_SetOutputReport(...)游戏手柄通过蓝牙连接时使用无效。我记得通过蓝牙读取DeviceIoControl(...)需要在输出缓冲区中存在附加参数。但我目前正试图找出一种通过DeviceIoControl(...). 如果您有任何建议,请随时发表评论!上面链接中的文章只激活了两个隆隆电机,但没有激活触发器!

编辑 2:我尝试DeviceIoControl(HANDLE, j, CHAR*, i, NULL, 0, DWORD*, NULL)从 j 0x0 扫描到 0xFFFFFFFF 和 i 0x1 到 0x3F。好吧,它起作用了...起初...但是经过几个 j 值后,我得到了蓝屏:(WDF_Violation至少我知道如何使计算机崩溃;))

于 2020-11-08T15:12:36.560 回答