[PATCH 4/4] hid: Implement HidP_SetScaledUsageValue.

Rémi Bernon rbernon at codeweavers.com
Wed Aug 25 11:08:33 CDT 2021


The function seems to actually do overflows and rounding errors.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---

It's so nasty that I'm not even sure anymore that I'll use it, but here
we go anyway.

 dlls/hid/hid.spec                    |  2 +-
 dlls/hid/hidp.c                      | 47 +++++++++++++++
 dlls/ntoskrnl.exe/tests/driver_hid.c | 10 ++++
 dlls/ntoskrnl.exe/tests/ntoskrnl.c   | 86 +++++++++++++++++++++++++---
 include/ddk/hidpi.h                  |  1 +
 5 files changed, 138 insertions(+), 8 deletions(-)

diff --git a/dlls/hid/hid.spec b/dlls/hid/hid.spec
index 5d4706e3727..37dfdbce307 100644
--- a/dlls/hid/hid.spec
+++ b/dlls/hid/hid.spec
@@ -35,7 +35,7 @@
 @ stdcall HidP_MaxDataListLength(long ptr)
 @ stdcall HidP_MaxUsageListLength(long long ptr)
 @ stub HidP_SetData
-@ stub HidP_SetScaledUsageValue
+@ stdcall HidP_SetScaledUsageValue(long long long long long ptr ptr long)
 @ stdcall HidP_SetUsageValue(long long long long long ptr ptr long)
 @ stdcall HidP_SetUsageValueArray(long long long long ptr long ptr ptr long)
 @ stdcall HidP_SetUsages(long long long ptr ptr ptr ptr long)
diff --git a/dlls/hid/hidp.c b/dlls/hid/hidp.c
index 8c43d27f252..cd9de2148c2 100644
--- a/dlls/hid/hidp.c
+++ b/dlls/hid/hidp.c
@@ -380,6 +380,53 @@ ULONG WINAPI HidP_MaxUsageListLength( HIDP_REPORT_TYPE report_type, USAGE usage_
     return count;
 }
 
+static NTSTATUS set_scaled_usage_value( const struct hid_value_caps *caps, void *user )
+{
+    ULONG bit_count = caps->bit_size * caps->report_count;
+    struct usage_value_params *params = user;
+    LONG value, log_range, phy_range;
+
+    if (caps->logical_min > caps->logical_max) return HIDP_STATUS_BAD_LOG_PHY_VALUES;
+    if (caps->physical_min > caps->physical_max) return HIDP_STATUS_BAD_LOG_PHY_VALUES;
+
+    if ((bit_count + 7) / 8 > sizeof(value)) return HIDP_STATUS_BUFFER_TOO_SMALL;
+    if (sizeof(LONG) > params->value_len) return HIDP_STATUS_BUFFER_TOO_SMALL;
+    value = *(LONG *)params->value_buf;
+
+    if (caps->physical_min || caps->physical_max)
+    {
+        /* testing shows that this is what the function does, including all
+         * the overflows and rounding errors... */
+        log_range = (caps->logical_max - caps->logical_min + 1) / 2;
+        phy_range = (caps->physical_max - caps->physical_min + 1) / 2;
+        value = value - caps->physical_min;
+        value = (log_range * value) / phy_range;
+        value = caps->logical_min + value;
+    }
+
+    copy_bits( params->report_buf, (unsigned char *)&value, bit_count, caps->start_bit );
+
+    return HIDP_STATUS_NULL;
+}
+
+NTSTATUS WINAPI HidP_SetScaledUsageValue( HIDP_REPORT_TYPE report_type, USAGE usage_page, USHORT collection,
+                                          USAGE usage, LONG value, PHIDP_PREPARSED_DATA preparsed_data,
+                                          char *report_buf, ULONG report_len )
+{
+    struct usage_value_params params = {.value_buf = &value, .value_len = sizeof(value), .report_buf = report_buf};
+    struct hid_preparsed_data *preparsed = (struct hid_preparsed_data *)preparsed_data;
+    struct caps_filter filter = {.values = TRUE, .usage_page = usage_page, .collection = collection, .usage = usage };
+    USHORT count = 1;
+
+    TRACE( "report_type %d, usage_page %x, collection %d, usage %x, value %d, preparsed_data %p, report_buf %p, report_len %u.\n",
+           report_type, usage_page, collection, usage, value, preparsed_data, report_buf, report_len );
+
+    if (!report_len) return HIDP_STATUS_INVALID_REPORT_LENGTH;
+
+    filter.report_id = report_buf[0];
+    return enum_value_caps( preparsed, report_type, report_len, &filter, set_scaled_usage_value, &params, &count );
+}
+
 static NTSTATUS set_usage_value( const struct hid_value_caps *caps, void *user )
 {
     struct usage_value_params *params = user;
diff --git a/dlls/ntoskrnl.exe/tests/driver_hid.c b/dlls/ntoskrnl.exe/tests/driver_hid.c
index ce942815dcb..ed0767877ac 100644
--- a/dlls/ntoskrnl.exe/tests/driver_hid.c
+++ b/dlls/ntoskrnl.exe/tests/driver_hid.c
@@ -356,6 +356,16 @@ static NTSTATUS WINAPI driver_internal_ioctl(DEVICE_OBJECT *device, IRP *irp)
                 /* reset global items */
                 UNIT(1, 0), /* None */
                 UNIT_EXPONENT(1, 0),
+
+                USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC),
+                USAGE(1, HID_USAGE_GENERIC_Z),
+                LOGICAL_MINIMUM(4, 0x0000),
+                LOGICAL_MAXIMUM(4, 0x7fff),
+                PHYSICAL_MINIMUM(4, 0xfff90000),
+                PHYSICAL_MAXIMUM(4, 0x0003ffff),
+                REPORT_SIZE(1, 32),
+                REPORT_COUNT(1, 1),
+                FEATURE(1, Data|Var|Abs),
             END_COLLECTION,
 
             USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC),
diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c
index 34491500423..84711f75b33 100644
--- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c
+++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c
@@ -1686,14 +1686,14 @@ static void test_hidp(HANDLE file, HANDLE async_file, int report_id, BOOL polled
             .UsagePage = HID_USAGE_PAGE_GENERIC,
             .InputReportByteLength = 26,
             .OutputReportByteLength = 3,
-            .FeatureReportByteLength = 18,
+            .FeatureReportByteLength = 22,
             .NumberLinkCollectionNodes = 10,
             .NumberInputButtonCaps = 17,
             .NumberInputValueCaps = 7,
             .NumberInputDataIndices = 47,
             .NumberFeatureButtonCaps = 1,
-            .NumberFeatureValueCaps = 5,
-            .NumberFeatureDataIndices = 7,
+            .NumberFeatureValueCaps = 6,
+            .NumberFeatureDataIndices = 8,
         },
         /* with report id */
         {
@@ -1701,14 +1701,14 @@ static void test_hidp(HANDLE file, HANDLE async_file, int report_id, BOOL polled
             .UsagePage = HID_USAGE_PAGE_GENERIC,
             .InputReportByteLength = 25,
             .OutputReportByteLength = 2,
-            .FeatureReportByteLength = 17,
+            .FeatureReportByteLength = 21,
             .NumberLinkCollectionNodes = 10,
             .NumberInputButtonCaps = 17,
             .NumberInputValueCaps = 7,
             .NumberInputDataIndices = 47,
             .NumberFeatureButtonCaps = 1,
-            .NumberFeatureValueCaps = 5,
-            .NumberFeatureDataIndices = 7,
+            .NumberFeatureValueCaps = 6,
+            .NumberFeatureDataIndices = 8,
         },
     };
     const HIDP_BUTTON_CAPS expect_button_caps[] =
@@ -2182,6 +2182,10 @@ static void test_hidp(HANDLE file, HANDLE async_file, int report_id, BOOL polled
                                       (LONG *)&value, preparsed_data, report, caps.InputReportByteLength);
     ok(status == HIDP_STATUS_BAD_LOG_PHY_VALUES, "HidP_GetScaledUsageValue returned %#x\n", status);
     ok(value == 0, "got value %x, expected %#x\n", value, 0);
+    status = HidP_SetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RY,
+                                      0, preparsed_data, report, caps.InputReportByteLength);
+    ok(status == HIDP_STATUS_BAD_LOG_PHY_VALUES, "HidP_GetScaledUsageValue returned %#x\n", status);
+    ok(value == 0, "got value %x, expected %#x\n", value, 0);
 
     value = HidP_MaxUsageListLength(HidP_Feature + 1, 0, preparsed_data);
     ok(value == 0, "HidP_MaxUsageListLength(HidP_Feature + 1, 0) returned %d, expected %d\n", value, 0);
@@ -2311,7 +2315,7 @@ static void test_hidp(HANDLE file, HANDLE async_file, int report_id, BOOL polled
     value = HidP_MaxDataListLength(HidP_Output, preparsed_data);
     ok(value == 0, "HidP_MaxDataListLength(HidP_Output) returned %d, expected %d\n", value, 0);
     value = HidP_MaxDataListLength(HidP_Feature, preparsed_data);
-    ok(value == 13, "HidP_MaxDataListLength(HidP_Feature) returned %d, expected %d\n", value, 13);
+    ok(value == 14, "HidP_MaxDataListLength(HidP_Feature) returned %d, expected %d\n", value, 14);
 
     value = 1;
     status = HidP_GetData(HidP_Input, data, &value, preparsed_data, report, caps.InputReportByteLength);
@@ -2474,6 +2478,74 @@ static void test_hidp(HANDLE file, HANDLE async_file, int report_id, BOOL polled
     ok(!memcmp(buffer, buffer + 16, 16), "unexpected report value\n");
 
 
+    value = 0x7fffffff;
+    status = HidP_SetUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                value, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_SetUsageValue returned %#x\n", status);
+    value = 0xdeadbeef;
+    status = HidP_GetScaledUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                      (LONG *)&value, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_VALUE_OUT_OF_RANGE, "HidP_GetScaledUsageValue returned %#x\n", status);
+    ok(value == 0, "got value %x, expected %#x\n", value, 0);
+    value = 0xdeadbeef;
+    status = HidP_GetUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                &value, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_GetUsageValue returned %#x\n", status);
+    ok(value == 0x7fffffff, "got value %x, expected %#x\n", value, 0x7fffffff);
+
+    value = 0x7fff;
+    status = HidP_SetUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                value, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_SetUsageValue returned %#x\n", status);
+    value = 0xdeadbeef;
+    status = HidP_GetScaledUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                      (LONG *)&value, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_GetScaledUsageValue returned %#x\n", status);
+    ok(value == 0x0003ffff, "got value %x, expected %#x\n", value, 0x0003ffff);
+
+    value = 0;
+    status = HidP_SetUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                value, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_SetUsageValue returned %#x\n", status);
+    value = 0xdeadbeef;
+    status = HidP_GetScaledUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                      (LONG *)&value, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_GetScaledUsageValue returned %#x\n", status);
+    ok(value == 0xfff90000, "got value %x, expected %#x\n", value, 0xfff90000);
+    status = HidP_SetScaledUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                      0x1000, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_SetScaledUsageValue returned %#x\n", status);
+    value = 0;
+    status = HidP_GetUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                &value, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_GetUsageValue returned %#x\n", status);
+    ok(value == 0xfffff518, "got value %x, expected %#x\n", value, 0xfffff518);
+    status = HidP_SetScaledUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                      0, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_SetScaledUsageValue returned %#x\n", status);
+    value = 0;
+    status = HidP_GetUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                &value, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_GetUsageValue returned %#x\n", status);
+    ok(value == 0xfffff45e, "got value %x, expected %#x\n", value, 0xfffff45e);
+    status = HidP_SetScaledUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                      0xdead, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_SetScaledUsageValue returned %#x\n", status);
+    value = 0;
+    status = HidP_GetUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                &value, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_GetUsageValue returned %#x\n", status);
+    ok(value == 0xfffffe7d, "got value %x, expected %#x\n", value, 0xfffffe7d);
+    status = HidP_SetScaledUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                      0xbeef, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_SetScaledUsageValue returned %#x\n", status);
+    value = 0;
+    status = HidP_GetUsageValue(HidP_Feature, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
+                                &value, preparsed_data, report, caps.FeatureReportByteLength);
+    ok(status == HIDP_STATUS_SUCCESS, "HidP_GetUsageValue returned %#x\n", status);
+    ok(value == 0xfffffd0b, "got value %x, expected %#x\n", value, 0xfffffd0b);
+
+
     memset(report, 0xcd, sizeof(report));
     status = HidP_InitializeReportForID(HidP_Input, report_id, preparsed_data, report, caps.InputReportByteLength);
     ok(status == HIDP_STATUS_SUCCESS, "HidP_InitializeReportForID returned %#x\n", status);
diff --git a/include/ddk/hidpi.h b/include/ddk/hidpi.h
index 305c38a95d3..e2e2bd2229b 100644
--- a/include/ddk/hidpi.h
+++ b/include/ddk/hidpi.h
@@ -202,6 +202,7 @@ NTSTATUS WINAPI HidP_GetValueCaps(HIDP_REPORT_TYPE ReportType, PHIDP_VALUE_CAPS
 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);
 NTSTATUS WINAPI HidP_GetScaledUsageValue(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE Usage, PLONG UsageValue, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength);
+NTSTATUS WINAPI HidP_SetScaledUsageValue(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE Usage, LONG UsageValue, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength);
 NTSTATUS WINAPI HidP_SetUsageValue(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE Usage, ULONG UsageValue, PHIDP_PREPARSED_DATA PreparsedData, CHAR *Report, ULONG ReportLength);
 NTSTATUS WINAPI HidP_SetUsageValueArray( HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection,
                                          USAGE Usage, PCHAR UsageValue, USHORT UsageValueByteLength,
-- 
2.33.0




More information about the wine-devel mailing list