[PATCH] hid: Implement HidP_GetUsageValueArray.

Gabriel Ivăncescu gabrielopcode at gmail.com
Wed Feb 26 07:03:46 CST 2020


Games like Risk of Rain 2 need this with certain controllers.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---

Andrew Eikum helped me test this out with a PS4 Controller on Windows
10. Using the test here, I took all the input reports from the Windows log
and sent them through this function in a simulation, to compare the outputs
with the dumped log from Windows. It was the same, so same input resulted
in same output.

 dlls/hid/hid.spec       |  2 +-
 dlls/hid/hidp.c         | 65 +++++++++++++++++++++++++++++++++++++++++
 dlls/hid/tests/device.c | 37 +++++++++++++++++++----
 include/ddk/hidpi.h     |  1 +
 4 files changed, 99 insertions(+), 6 deletions(-)

diff --git a/dlls/hid/hid.spec b/dlls/hid/hid.spec
index b8e29fe..98508a4 100644
--- a/dlls/hid/hid.spec
+++ b/dlls/hid/hid.spec
@@ -27,7 +27,7 @@
 @ stdcall HidP_GetSpecificButtonCaps(long long long long ptr ptr ptr)
 @ stdcall HidP_GetSpecificValueCaps(long long long long ptr ptr ptr)
 @ stdcall HidP_GetUsageValue(long long long long ptr ptr ptr long)
-@ stub HidP_GetUsageValueArray
+@ stdcall HidP_GetUsageValueArray(long long long long ptr long ptr ptr long)
 @ stdcall HidP_GetUsages(long long long ptr ptr ptr ptr long)
 @ stdcall HidP_GetUsagesEx(long long ptr ptr ptr ptr long)
 @ stdcall HidP_GetValueCaps(long ptr ptr ptr)
diff --git a/dlls/hid/hidp.c b/dlls/hid/hidp.c
index 817e021..c4d1626 100644
--- a/dlls/hid/hidp.c
+++ b/dlls/hid/hidp.c
@@ -117,6 +117,46 @@ static NTSTATUS set_report_data(BYTE *report, INT reportLength, INT startBit, IN
     return HIDP_STATUS_SUCCESS;
 }
 
+static NTSTATUS get_report_data_array(BYTE *report, UINT reportLength, UINT startBit, UINT elemSize,
+                                      UINT numElements, PCHAR values, UINT valuesSize)
+{
+    BYTE byte, *end, *p = report + startBit / 8;
+    ULONG size = elemSize * numElements;
+    ULONG m, bit_index = startBit % 8;
+    BYTE *data = (BYTE*)values;
+
+    if ((startBit + size) / 8 > reportLength)
+        return HIDP_STATUS_INVALID_REPORT_LENGTH;
+
+    if (valuesSize < (size + 7) / 8)
+        return HIDP_STATUS_BUFFER_TOO_SMALL;
+
+    end = report + (startBit + size + 7) / 8;
+
+    data--;
+    byte = *p++;
+    while (p != end)
+    {
+        *(++data) = byte >> bit_index;
+        byte = *p++;
+        *data |= byte << (8 - bit_index);
+    }
+
+    /* Handle the end and mask out bits beyond */
+    m = (startBit + size) % 8;
+    m = m ? m : 8;
+
+    if (m > bit_index)
+        *(++data) = (byte >> bit_index) & ((1 << (m - bit_index)) - 1);
+    else
+        *data &= (1 << (m + 8 - bit_index)) - 1;
+
+    if (++data < (BYTE*)values + valuesSize)
+        memset(data, 0, (BYTE*)values + valuesSize - data);
+
+    return HIDP_STATUS_SUCCESS;
+}
+
 
 NTSTATUS WINAPI HidP_GetButtonCaps(HIDP_REPORT_TYPE ReportType, PHIDP_BUTTON_CAPS ButtonCaps,
                                    PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData)
@@ -325,6 +365,31 @@ NTSTATUS WINAPI HidP_GetUsageValue(HIDP_REPORT_TYPE ReportType, USAGE UsagePage,
 }
 
 
+NTSTATUS WINAPI HidP_GetUsageValueArray(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection,
+                                        USAGE Usage, PCHAR UsageValue, USHORT UsageValueByteLength,
+                                        PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength)
+{
+    WINE_HID_ELEMENT element;
+    NTSTATUS rc;
+
+    TRACE("(%i, %x, %i, %i, %p, %u, %p, %p, %i)\n", ReportType, UsagePage, LinkCollection, Usage, UsageValue,
+          UsageValueByteLength, PreparsedData, Report, ReportLength);
+
+    rc = find_usage(ReportType, UsagePage, LinkCollection, Usage, PreparsedData, Report, ValueElement, &element);
+
+    if (rc == HIDP_STATUS_SUCCESS)
+    {
+        if (element.caps.value.IsRange || element.caps.value.ReportCount <= 1 || !element.bitCount)
+            return HIDP_STATUS_NOT_VALUE_ARRAY;
+
+        return get_report_data_array((BYTE*)Report, ReportLength, element.valueStartBit, element.bitCount,
+                                     element.caps.value.ReportCount, UsageValue, UsageValueByteLength);
+    }
+
+    return rc;
+}
+
+
 NTSTATUS WINAPI HidP_GetUsages(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection,
                                PUSAGE UsageList, PULONG UsageLength, PHIDP_PREPARSED_DATA PreparsedData,
                                PCHAR Report, ULONG ReportLength)
diff --git a/dlls/hid/tests/device.c b/dlls/hid/tests/device.c
index 24c3077..3dd22b7 100644
--- a/dlls/hid/tests/device.c
+++ b/dlls/hid/tests/device.c
@@ -253,11 +253,38 @@ static void process_data(HIDP_CAPS Caps, PHIDP_PREPARSED_DATA ppd, CHAR *data, D
         trace("\tValues:\n");
         for (i = 0; i < length; i++)
         {
-            status = HidP_GetUsageValue(HidP_Input, values[i].UsagePage, 0,
-                values[i].Range.UsageMin, &value, ppd, data, data_length);
-            ok(status == HIDP_STATUS_SUCCESS, "Failed to get value [%i,%i] (%x)\n",
-                values[i].UsagePage, values[i].Range.UsageMin, status);
-            trace("[%02x, %02x]: %u\n",values[i].UsagePage, values[i].Range.UsageMin, value);
+            ok(values[i].ReportCount, "Zero ReportCount for [%i,%i]\n", values[i].UsagePage, values[i].NotRange.Usage);
+            if (values[i].IsRange || values[i].ReportCount <= 1)
+            {
+                status = HidP_GetUsageValue(HidP_Input, values[i].UsagePage, 0,
+                    values[i].Range.UsageMin, &value, ppd, data, data_length);
+                ok(status == HIDP_STATUS_SUCCESS, "Failed to get value [%i,%i] (%x)\n",
+                    values[i].UsagePage, values[i].Range.UsageMin, status);
+                trace("[%02x, %02x]: %u\n", values[i].UsagePage, values[i].Range.UsageMin, value);
+            }
+            else
+            {
+                USHORT k, array_size = (values[i].BitSize * values[i].ReportCount + 7) / 8;
+                PCHAR array = HeapAlloc(GetProcessHeap(), 0, array_size);
+                char *dump = HeapAlloc(GetProcessHeap(), 0, array_size * 3 + 1);
+
+                status = HidP_GetUsageValueArray(HidP_Input, values[i].UsagePage, 0,
+                    values[i].NotRange.Usage, array, array_size, ppd, data, data_length);
+                ok(status == HIDP_STATUS_SUCCESS, "Failed to get value array [%i,%i] (%x)\n",
+                    values[i].UsagePage, values[i].NotRange.Usage, status);
+                dump[0] = 0;
+                for (k = 0; k < array_size; k++)
+                {
+                    char bytestr[5];
+                    sprintf(bytestr, " %02x", (BYTE)array[k]);
+                    strcat(dump, bytestr);
+                }
+                trace("[%02x, %02x] element bit size %u num elements %u:%s\n", values[i].UsagePage,
+                    values[i].NotRange.Usage, values[i].BitSize, values[i].ReportCount, dump);
+
+                HeapFree(GetProcessHeap(), 0, dump);
+                HeapFree(GetProcessHeap(), 0, array);
+            }
         }
 
         HeapFree(GetProcessHeap(), 0, values);
diff --git a/include/ddk/hidpi.h b/include/ddk/hidpi.h
index 51d61ea..fb497f3 100644
--- a/include/ddk/hidpi.h
+++ b/include/ddk/hidpi.h
@@ -195,6 +195,7 @@ NTSTATUS WINAPI HidP_GetButtonCaps(HIDP_REPORT_TYPE ReportType, PHIDP_BUTTON_CAP
 NTSTATUS WINAPI HidP_GetCaps(PHIDP_PREPARSED_DATA PreparsedData, PHIDP_CAPS Capabilities);
 NTSTATUS WINAPI HidP_GetUsages(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, PUSAGE UsageList, PULONG UsageLength, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength);
 NTSTATUS WINAPI HidP_GetUsageValue(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE Usage, PULONG UsageValue, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength);
+NTSTATUS WINAPI HidP_GetUsageValueArray(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE Usage, PCHAR UsageValue, USHORT UsageValueByteLength, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength);
 NTSTATUS WINAPI HidP_GetValueCaps(HIDP_REPORT_TYPE ReportType, PHIDP_VALUE_CAPS ValueCaps, PUSHORT ValueCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
 NTSTATUS WINAPI HidP_InitializeReportForID(HIDP_REPORT_TYPE ReportType, UCHAR ReportID, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength);
 ULONG WINAPI HidP_MaxUsageListLength(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, PHIDP_PREPARSED_DATA PreparsedData);
-- 
2.21.0




More information about the wine-devel mailing list