[PATCH 1/5] dinput8/tests: Add tests for the internal HID preparsed data structures.

Rémi Bernon rbernon at codeweavers.com
Wed Sep 15 02:29:49 CDT 2021


Correct DInput implementation will require it to iterate on the internal
HID structures, instead of using the higher-level value / button caps
abstraction.

This will make Wine DInput incompatible with native HID stack, unless
the internal structure match.

These tests exhibit native internal structures, which are close to what
Wine uses, so it should not be too complicated to restore compatibility.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/dinput8/tests/hid.c | 470 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 470 insertions(+)

diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c
index 86e878d5ecb..cb1f4d61b7a 100644
--- a/dlls/dinput8/tests/hid.c
+++ b/dlls/dinput8/tests/hid.c
@@ -2710,6 +2710,475 @@ static void test_hid_driver( DWORD report_id, DWORD polled )
     SetCurrentDirectoryW( cwd );
 }
 
+/* undocumented HID internal preparsed data structure */
+
+struct hidp_kdr_caps
+{
+    USHORT usage_page;
+    UCHAR report_id;
+    UCHAR start_bit;
+    USHORT bit_size;
+    USHORT report_count;
+    USHORT start_byte;
+    USHORT total_bits;
+    ULONG bit_field;
+    USHORT end_byte;
+    USHORT link_collection;
+    USAGE link_usage_page;
+    USAGE link_usage;
+    ULONG flags;
+    ULONG padding[8];
+    USAGE usage_min;
+    USAGE usage_max;
+    USHORT string_min;
+    USHORT string_max;
+    USHORT designator_min;
+    USHORT designator_max;
+    USHORT data_index_min;
+    USHORT data_index_max;
+    USHORT null_value;
+    USHORT unknown;
+    LONG logical_min;
+    LONG logical_max;
+    LONG physical_min;
+    LONG physical_max;
+    LONG units;
+    LONG units_exp;
+};
+
+/* named array continues on next caps */
+#define HIDP_KDR_CAPS_ARRAY_HAS_MORE       0x01
+#define HIDP_KDR_CAPS_IS_CONSTANT          0x02
+#define HIDP_KDR_CAPS_IS_BUTTON            0x04
+#define HIDP_KDR_CAPS_IS_ABSOLUTE          0x08
+#define HIDP_KDR_CAPS_IS_RANGE             0x10
+#define HIDP_KDR_CAPS_IS_STRING_RANGE      0x40
+#define HIDP_KDR_CAPS_IS_DESIGNATOR_RANGE  0x80
+
+struct hidp_kdr_node
+{
+    USAGE usage;
+    USAGE usage_page;
+    USHORT parent;
+    USHORT number_of_children;
+    USHORT next_sibling;
+    USHORT first_child;
+    ULONG collection_type;
+};
+
+struct hidp_kdr
+{
+    char magic[8];
+    USAGE usage;
+    USAGE usage_page;
+    USHORT unknown[2];
+    USHORT input_caps_start;
+    USHORT input_caps_count;
+    USHORT input_caps_end;
+    USHORT input_report_byte_length;
+    USHORT output_caps_start;
+    USHORT output_caps_count;
+    USHORT output_caps_end;
+    USHORT output_report_byte_length;
+    USHORT feature_caps_start;
+    USHORT feature_caps_count;
+    USHORT feature_caps_end;
+    USHORT feature_report_byte_length;
+    USHORT caps_size;
+    USHORT number_link_collection_nodes;
+    struct hidp_kdr_caps caps[1];
+    /* struct hidp_kdr_node nodes[1] */
+};
+
+static void test_hidp_kdr(void)
+{
+#include "psh_hid_macros.h"
+    const unsigned char report_desc[] =
+    {
+        USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC),
+        USAGE(1, HID_USAGE_GENERIC_JOYSTICK),
+        COLLECTION(1, Application),
+            USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC),
+            LOGICAL_MINIMUM(1, 1),
+            LOGICAL_MAXIMUM(1, 127),
+            PHYSICAL_MINIMUM(1, -128),
+            PHYSICAL_MAXIMUM(1, 127),
+
+            USAGE(1, HID_USAGE_GENERIC_RZ),
+            REPORT_SIZE(1, 16),
+            REPORT_COUNT(1, 0),
+            FEATURE(1, Data|Var|Abs),
+            USAGE(1, HID_USAGE_GENERIC_SLIDER),
+            REPORT_SIZE(1, 16),
+            REPORT_COUNT(1, 1),
+            FEATURE(1, Data|Var|Abs),
+
+            USAGE(1, HID_USAGE_GENERIC_X),
+            REPORT_SIZE(1, 8),
+            REPORT_COUNT(1, 1),
+            UNIT(1, 0x100e),
+            UNIT_EXPONENT(1, -3),
+            INPUT(1, Data|Var|Abs),
+            UNIT_EXPONENT(1, 0),
+            UNIT(1, 0),
+            USAGE(1, HID_USAGE_GENERIC_Y),
+            DESIGNATOR_MINIMUM(1, 1),
+            DESIGNATOR_MAXIMUM(1, 4),
+            REPORT_SIZE(1, 8),
+            REPORT_COUNT(1, 1),
+            INPUT(1, Cnst|Var|Abs),
+            USAGE(1, HID_USAGE_GENERIC_Z),
+            REPORT_SIZE(1, 8),
+            REPORT_COUNT(1, 1),
+            INPUT(1, Data|Var|Rel),
+            USAGE(1, HID_USAGE_GENERIC_RX),
+            USAGE(1, HID_USAGE_GENERIC_RY),
+            REPORT_SIZE(1, 16),
+            REPORT_COUNT(1, 2),
+            LOGICAL_MINIMUM(1, 7),
+            INPUT(1, Data|Var|Abs|Null),
+
+            COLLECTION(1, Application),
+                USAGE(4, (HID_USAGE_PAGE_BUTTON << 16)|1),
+                USAGE(4, (HID_USAGE_PAGE_BUTTON << 16)|2),
+                REPORT_SIZE(1, 1),
+                REPORT_COUNT(1, 8),
+                LOGICAL_MINIMUM(1, 0),
+                LOGICAL_MAXIMUM(1, 1),
+                INPUT(1, Data|Var|Abs),
+
+                USAGE_MINIMUM(4, (HID_USAGE_PAGE_BUTTON << 16)|3),
+                USAGE_MAXIMUM(4, (HID_USAGE_PAGE_BUTTON << 16)|8),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                LOGICAL_MINIMUM(1, 3),
+                LOGICAL_MAXIMUM(1, 8),
+                INPUT(1, Data|Ary|Abs),
+
+                USAGE_MINIMUM(4, (HID_USAGE_PAGE_BUTTON << 16)|9),
+                USAGE_MAXIMUM(4, (HID_USAGE_PAGE_BUTTON << 16)|12),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 4),
+                LOGICAL_MINIMUM(1, 9),
+                LOGICAL_MAXIMUM(1, 12),
+                INPUT(2, Data|Ary|Abs|Buff),
+
+                USAGE(4, (HID_USAGE_PAGE_BUTTON << 16)|13),
+                USAGE(4, (HID_USAGE_PAGE_BUTTON << 16)|14),
+                USAGE(4, (HID_USAGE_PAGE_BUTTON << 16)|15),
+                USAGE(4, (HID_USAGE_PAGE_BUTTON << 16)|16),
+                REPORT_SIZE(1, 8),
+                REPORT_COUNT(1, 1),
+                LOGICAL_MINIMUM(1, 13),
+                LOGICAL_MAXIMUM(1, 16),
+                INPUT(1, Data|Ary|Abs),
+            END_COLLECTION,
+        END_COLLECTION,
+    };
+#include "pop_hid_macros.h"
+
+    static const HIDP_CAPS expect_hidp_caps =
+    {
+        .Usage = HID_USAGE_GENERIC_JOYSTICK,
+        .UsagePage = HID_USAGE_PAGE_GENERIC,
+        .InputReportByteLength = 15,
+    };
+    static const HID_DEVICE_ATTRIBUTES attributes =
+    {
+        .Size = sizeof(HID_DEVICE_ATTRIBUTES),
+        .VendorID = 0x1209,
+        .ProductID = 0x0001,
+        .VersionNumber = 0x0100,
+    };
+    static const struct hidp_kdr expect_kdr =
+    {
+        .magic = "HidP KDR",
+        .usage = 0x04,
+        .usage_page = 0x01,
+        .input_caps_count = 13,
+        .input_caps_end = 13,
+        .input_report_byte_length = 15,
+        .output_caps_start = 13,
+        .output_caps_end = 13,
+        .feature_caps_start = 13,
+        .feature_caps_count = 2,
+        .feature_caps_end = 14,
+        .feature_report_byte_length = 3,
+        .caps_size = 1560,
+        .number_link_collection_nodes = 2,
+    };
+    static const struct hidp_kdr_caps expect_caps[] =
+    {
+        {
+            .usage_page = 0x01, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0x1, .total_bits = 0x08,
+            .bit_field = 0x002, .end_byte = 0x2, .link_usage_page = 0x01, .link_usage = 0x04, .flags = 0x08,
+            .usage_min = 0x30, .usage_max = 0x30, .logical_min = 1, .logical_max = 127, .physical_min = -128,
+            .physical_max = 127, .units = 0xe, .units_exp = -3
+        },
+        {
+            .usage_page = 0x01, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0x2, .total_bits = 0x08,
+            .bit_field = 0x003, .end_byte = 0x3, .link_usage_page = 0x01, .link_usage = 0x04, .flags = 0x8a,
+            .usage_min = 0x31, .usage_max = 0x31, .designator_min = 1, .designator_max = 4, .data_index_min = 0x01,
+            .data_index_max = 0x01, .logical_min = 1, .logical_max = 127, .physical_min = -128, .physical_max = 127
+        },
+        {
+            .usage_page = 0x01, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0x3, .total_bits = 0x08,
+            .bit_field = 0x006, .end_byte = 0x4, .link_usage_page = 0x01, .link_usage = 0x04, .usage_min = 0x32,
+            .usage_max = 0x32, .data_index_min = 0x02, .data_index_max = 0x02, .logical_min = 1, .logical_max = 127,
+            .physical_min = -128, .physical_max = 127
+        },
+        {
+            .usage_page = 0x01, .bit_size = 0x10, .report_count = 0x1, .start_byte = 0x6, .total_bits = 0x10,
+            .bit_field = 0x042, .end_byte = 0x8, .link_usage_page = 0x01, .link_usage = 0x04, .flags = 0x08,
+            .usage_min = 0x34, .usage_max = 0x34, .data_index_min = 0x03, .data_index_max = 0x03, .null_value = 1,
+            .logical_min = 7, .logical_max = 127, .physical_min = -128, .physical_max = 127
+        },
+        {
+            .usage_page = 0x01, .bit_size = 0x10, .report_count = 0x1, .start_byte = 0x4, .total_bits = 0x10,
+            .bit_field = 0x042, .end_byte = 0x6, .link_usage_page = 0x01, .link_usage = 0x04, .flags = 0x08,
+            .usage_min = 0x33, .usage_max = 0x33, .data_index_min = 0x04, .data_index_max = 0x04, .null_value = 1,
+            .logical_min = 7, .logical_max = 127, .physical_min = -128, .physical_max = 127
+        },
+        {
+            .usage_page = 0x09, .start_bit = 1, .bit_size = 0x01, .report_count = 0x7, .start_byte = 0x8, .total_bits = 0x07,
+            .bit_field = 0x002, .end_byte = 0x9, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x0c,
+            .usage_min = 0x02, .usage_max = 0x02, .data_index_min = 0x05, .data_index_max = 0x05,
+        },
+        {
+            .usage_page = 0x09, .bit_size = 0x01, .report_count = 0x1, .start_byte = 0x8, .total_bits = 0x01,
+            .bit_field = 0x002, .end_byte = 0x9, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x0c,
+            .usage_min = 0x01, .usage_max = 0x01, .data_index_min = 0x06, .data_index_max = 0x06,
+        },
+        {
+            .usage_page = 0x09, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0x9, .total_bits = 0x08,
+            .bit_field = 0x000, .end_byte = 0xa, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x1c,
+            .usage_min = 0x03, .usage_max = 0x08, .data_index_min = 0x07, .data_index_max = 0x0c, .null_value = 3,
+            .logical_min = 8
+        },
+        {
+            .usage_page = 0x09, .bit_size = 0x08, .report_count = 0x4, .start_byte = 0xa, .total_bits = 0x20,
+            .bit_field = 0x100, .end_byte = 0xe, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x1c,
+            .usage_min = 0x09, .usage_max = 0x0c, .data_index_min = 0x0d, .data_index_max = 0x10, .null_value = 9,
+            .logical_min = 12
+        },
+        {
+            .usage_page = 0x09, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0xe, .total_bits = 0x08,
+            .bit_field = 0x000, .end_byte = 0xf, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x0d,
+            .usage_min = 0x10, .usage_max = 0x10, .data_index_min = 0x14, .data_index_max = 0x14, .null_value = 13,
+            .logical_min = 16
+        },
+        {
+            .usage_page = 0x09, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0xe, .total_bits = 0x08,
+            .bit_field = 0x000, .end_byte = 0xf, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x0d,
+            .usage_min = 0x0f, .usage_max = 0x0f, .data_index_min = 0x13, .data_index_max = 0x13, .null_value = 13,
+            .logical_min = 16
+        },
+        {
+            .usage_page = 0x09, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0xe, .total_bits = 0x08,
+            .bit_field = 0x000, .end_byte = 0xf, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x0d,
+            .usage_min = 0x0e, .usage_max = 0x0e, .data_index_min = 0x12, .data_index_max = 0x12, .null_value = 13,
+            .logical_min = 16
+        },
+        {
+            .usage_page = 0x09, .bit_size = 0x08, .report_count = 0x1, .start_byte = 0xe, .total_bits = 0x08,
+            .bit_field = 0x000, .end_byte = 0xf, .link_collection = 1, .link_usage_page = 0x01, .flags = 0x0c,
+            .usage_min = 0x0d, .usage_max = 0x0d, .data_index_min = 0x11, .data_index_max = 0x11, .null_value = 13,
+            .logical_min = 16
+        },
+        {
+            .usage_page = 0x01, .bit_size = 0x10, .report_count = 0x1, .start_byte = 0x1, .total_bits = 0x10,
+            .bit_field = 0x002, .end_byte = 0x3, .link_usage_page = 0x01, .link_usage = 0x04, .flags = 0x08,
+            .usage_min = 0x36, .usage_max = 0x36, .logical_min = 1, .logical_max = 127, .physical_min = -128,
+            .physical_max = 127
+        },
+        {
+        },
+    };
+    static const struct hidp_kdr_node expect_nodes[] =
+    {
+        {
+            .usage = 0x04,
+            .usage_page = 0x01,
+            .parent = 0,
+            .number_of_children = 0x1,
+            .next_sibling = 0,
+            .first_child = 0x1,
+            .collection_type = 0x1,
+        },
+        {
+            .usage = 0x00,
+            .usage_page = 0x01,
+            .parent = 0,
+            .number_of_children = 0,
+            .next_sibling = 0,
+            .first_child = 0,
+            .collection_type = 0x1,
+        },
+    };
+
+    char buffer[FIELD_OFFSET( SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath[MAX_PATH] )];
+    SP_DEVICE_INTERFACE_DATA iface = {sizeof(SP_DEVICE_INTERFACE_DATA)};
+    SP_DEVICE_INTERFACE_DETAIL_DATA_W *iface_detail = (void *)buffer;
+    SP_DEVINFO_DATA device = {sizeof(SP_DEVINFO_DATA)};
+    WCHAR cwd[MAX_PATH], tempdir[MAX_PATH];
+    PHIDP_PREPARSED_DATA preparsed_data;
+    DWORD i, report_id = 0, polled = 0;
+    struct hidp_kdr *kdr;
+    LSTATUS status;
+    HDEVINFO set;
+    HANDLE file;
+    HKEY hkey;
+    BOOL ret;
+
+    GetCurrentDirectoryW( ARRAY_SIZE(cwd), cwd );
+    GetTempPathW( ARRAY_SIZE(tempdir), tempdir );
+    SetCurrentDirectoryW( tempdir );
+
+    status = RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services\\winetest",
+                              0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL );
+    ok( !status, "RegCreateKeyExW returned %#x\n", status );
+
+    status = RegSetValueExW( hkey, L"ReportID", 0, REG_DWORD, (void *)&report_id, sizeof(report_id) );
+    ok( !status, "RegSetValueExW returned %#x\n", status );
+
+    status = RegSetValueExW( hkey, L"PolledMode", 0, REG_DWORD, (void *)&polled, sizeof(polled) );
+    ok( !status, "RegSetValueExW returned %#x\n", status );
+
+    status = RegSetValueExW( hkey, L"Descriptor", 0, REG_BINARY, (void *)report_desc, sizeof(report_desc) );
+    ok( !status, "RegSetValueExW returned %#x\n", status );
+
+    status = RegSetValueExW( hkey, L"Attributes", 0, REG_BINARY, (void *)&attributes, sizeof(attributes) );
+    ok( !status, "RegSetValueExW returned %#x\n", status );
+
+    status = RegSetValueExW( hkey, L"Caps", 0, REG_BINARY, (void *)&expect_hidp_caps, sizeof(expect_hidp_caps) );
+    ok( !status, "RegSetValueExW returned %#x\n", status );
+
+    status = RegSetValueExW( hkey, L"Expect", 0, REG_BINARY, NULL, 0 );
+    ok( !status, "RegSetValueExW returned %#x\n", status );
+
+    status = RegSetValueExW( hkey, L"Input", 0, REG_BINARY, NULL, 0 );
+    ok( !status, "RegSetValueExW returned %#x\n", status );
+
+    if (!pnp_driver_start( L"driver_hid.dll" )) goto done;
+
+    set = SetupDiGetClassDevsW( &GUID_DEVINTERFACE_HID, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT );
+    ok( set != INVALID_HANDLE_VALUE, "failed to get device list, error %#x\n", GetLastError() );
+    for (i = 0; SetupDiEnumDeviceInfo( set, i, &device ); ++i)
+    {
+        ret = SetupDiEnumDeviceInterfaces( set, &device, &GUID_DEVINTERFACE_HID, 0, &iface );
+        ok( ret, "failed to get interface, error %#x\n", GetLastError() );
+        ok( IsEqualGUID( &iface.InterfaceClassGuid, &GUID_DEVINTERFACE_HID ), "wrong class %s\n",
+            debugstr_guid( &iface.InterfaceClassGuid ) );
+        ok( iface.Flags == SPINT_ACTIVE, "got flags %#x\n", iface.Flags );
+
+        iface_detail->cbSize = sizeof(*iface_detail);
+        ret = SetupDiGetDeviceInterfaceDetailW( set, &iface, iface_detail, sizeof(buffer), NULL, NULL );
+        ok( ret, "failed to get interface path, error %#x\n", GetLastError() );
+
+        if (wcsstr( iface_detail->DevicePath, L"\\\\?\\hid#winetest#1" )) break;
+    }
+    SetupDiDestroyDeviceInfoList( set );
+
+    file = CreateFileW( iface_detail->DevicePath, FILE_READ_ACCESS | FILE_WRITE_ACCESS,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
+    ok( file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError() );
+
+    ret = HidD_GetPreparsedData( file, &preparsed_data );
+    ok( ret, "HidD_GetPreparsedData failed with error %u\n", GetLastError() );
+
+    kdr = (struct hidp_kdr *)preparsed_data;
+    todo_wine
+    ok( !strncmp( kdr->magic, expect_kdr.magic, 8 ), "got %s expected %s\n",
+        debugstr_an(kdr->magic, 8), debugstr_an(expect_kdr.magic, 8) );
+
+    if (!strncmp( kdr->magic, expect_kdr.magic, 8 ))
+    {
+        check_member( *kdr, expect_kdr, "%04x", usage );
+        check_member( *kdr, expect_kdr, "%04x", usage_page );
+        check_member( *kdr, expect_kdr, "%#x", unknown[0] );
+        check_member( *kdr, expect_kdr, "%#x", unknown[1] );
+        check_member( *kdr, expect_kdr, "%d", input_caps_start );
+        check_member( *kdr, expect_kdr, "%d", input_caps_count );
+        check_member( *kdr, expect_kdr, "%d", input_caps_end );
+        check_member( *kdr, expect_kdr, "%d", input_report_byte_length );
+        check_member( *kdr, expect_kdr, "%d", output_caps_start );
+        check_member( *kdr, expect_kdr, "%d", output_caps_count );
+        check_member( *kdr, expect_kdr, "%d", output_caps_end );
+        check_member( *kdr, expect_kdr, "%d", output_report_byte_length );
+        check_member( *kdr, expect_kdr, "%d", feature_caps_start );
+        check_member( *kdr, expect_kdr, "%d", feature_caps_count );
+        check_member( *kdr, expect_kdr, "%d", feature_caps_end );
+        check_member( *kdr, expect_kdr, "%d", feature_report_byte_length );
+        check_member( *kdr, expect_kdr, "%d", caps_size );
+        check_member( *kdr, expect_kdr, "%d", number_link_collection_nodes );
+
+        for (i = 0; i < ARRAY_SIZE(expect_caps); ++i)
+        {
+            winetest_push_context( "caps[%d]", i );
+            check_member( kdr->caps[i], expect_caps[i], "%04x", usage_page );
+            check_member( kdr->caps[i], expect_caps[i], "%d", report_id );
+            check_member( kdr->caps[i], expect_caps[i], "%d", start_bit );
+            check_member( kdr->caps[i], expect_caps[i], "%d", bit_size );
+            check_member( kdr->caps[i], expect_caps[i], "%d", report_count );
+            check_member( kdr->caps[i], expect_caps[i], "%d", start_byte );
+            check_member( kdr->caps[i], expect_caps[i], "%d", total_bits );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", bit_field );
+            check_member( kdr->caps[i], expect_caps[i], "%d", end_byte );
+            check_member( kdr->caps[i], expect_caps[i], "%d", link_collection );
+            check_member( kdr->caps[i], expect_caps[i], "%04x", link_usage_page );
+            check_member( kdr->caps[i], expect_caps[i], "%04x", link_usage );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", flags );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", padding[0] );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", padding[1] );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", padding[2] );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", padding[3] );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", padding[4] );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", padding[5] );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", padding[6] );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", padding[7] );
+            check_member( kdr->caps[i], expect_caps[i], "%04x", usage_min );
+            check_member( kdr->caps[i], expect_caps[i], "%04x", usage_max );
+            check_member( kdr->caps[i], expect_caps[i], "%d", string_min );
+            check_member( kdr->caps[i], expect_caps[i], "%d", string_max );
+            check_member( kdr->caps[i], expect_caps[i], "%d", designator_min );
+            check_member( kdr->caps[i], expect_caps[i], "%d", designator_max );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", data_index_min );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", data_index_max );
+            check_member( kdr->caps[i], expect_caps[i], "%d", null_value );
+            check_member( kdr->caps[i], expect_caps[i], "%d", unknown );
+            check_member( kdr->caps[i], expect_caps[i], "%d", logical_min );
+            check_member( kdr->caps[i], expect_caps[i], "%d", logical_max );
+            check_member( kdr->caps[i], expect_caps[i], "%d", physical_min );
+            check_member( kdr->caps[i], expect_caps[i], "%d", physical_max );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", units );
+            check_member( kdr->caps[i], expect_caps[i], "%#x", units_exp );
+            winetest_pop_context();
+        }
+
+        for (i = 0; i < ARRAY_SIZE(expect_nodes); ++i)
+        {
+            struct hidp_kdr_node *nodes = (struct hidp_kdr_node *)((char *)kdr->caps + kdr->caps_size);
+            winetest_push_context( "nodes[%d]", i );
+            check_member( nodes[i], expect_nodes[i], "%04x", usage );
+            check_member( nodes[i], expect_nodes[i], "%04x", usage_page );
+            check_member( nodes[i], expect_nodes[i], "%d", parent );
+            check_member( nodes[i], expect_nodes[i], "%d", number_of_children );
+            check_member( nodes[i], expect_nodes[i], "%d", next_sibling );
+            check_member( nodes[i], expect_nodes[i], "%d", first_child );
+            check_member( nodes[i], expect_nodes[i], "%#x", collection_type );
+            winetest_pop_context();
+        }
+    }
+
+    HidD_FreePreparsedData( preparsed_data );
+
+    CloseHandle( file );
+
+done:
+    pnp_driver_stop();
+    SetCurrentDirectoryW( cwd );
+}
+
 START_TEST( hid )
 {
     HANDLE mapping;
@@ -2741,6 +3210,7 @@ START_TEST( hid )
     ok( okfile != INVALID_HANDLE_VALUE, "failed to create file, error %u\n", GetLastError() );
 
     subtest( "driver_hid" );
+    test_hidp_kdr();
     test_hid_driver( 0, FALSE );
     test_hid_driver( 1, FALSE );
     test_hid_driver( 0, TRUE );
-- 
2.33.0




More information about the wine-devel mailing list