Based on a patch from Oleg Makarenko.
Supersedes https://gitlab.winehq.org/wine/wine/-/merge_requests/8509
From: Rémi Bernon rbernon@codeweavers.com
Based on a patch from Oleg Makarenko. --- dlls/dinput/tests/hid.c | 79 +++++++++++++++++++++++++ dlls/hid/hid.spec | 2 +- dlls/hid/hidp.c | 90 +++++++++++++++++++++++++++++ dlls/hidparse.sys/hidparse.sys.spec | 2 +- include/ddk/hidpi.h | 1 + 5 files changed, 172 insertions(+), 2 deletions(-)
diff --git a/dlls/dinput/tests/hid.c b/dlls/dinput/tests/hid.c index 7ec8482aeed..c9cdb649f26 100644 --- a/dlls/dinput/tests/hid.c +++ b/dlls/dinput/tests/hid.c @@ -1619,6 +1619,24 @@ static void test_hidp( HANDLE file, HANDLE async_file, int report_id, BOOL polle { .DataIndex = 37, .RawValue = 1, }, { .DataIndex = 39, .RawValue = 1, }, }; + const BYTE expect_report_0[] = + { + report_id, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const BYTE expect_report_1[] = + { + report_id, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const BYTE expect_report_2[] = + { + report_id, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + };
DWORD waveform_list, collection_count, generic_output_list; OVERLAPPED overlapped = {0}, overlapped2 = {0}; @@ -2116,6 +2134,67 @@ static void test_hidp( HANDLE file, HANDLE async_file, int report_id, BOOL polle winetest_pop_context(); }
+ memset( report, 0, caps.InputReportByteLength ); + report[0] = report_id; + + value = 0; + status = HidP_SetData( HidP_Input, data, &value, preparsed_data, report, caps.InputReportByteLength ); + ok( status == HIDP_STATUS_REPORT_DOES_NOT_EXIST, "HidP_SetData returned %#lx\n", status ); + ok( value == 0, "got data count %ld, expected %d\n", value, 0 ); + + value = 0; + data[value].DataIndex = 0; data[value++].RawValue = 1; + data[value].DataIndex = 1; data[value++].RawValue = 2; + data[value].DataIndex = 0; data[value++].RawValue = 3; + data[value].DataIndex = 43; data[value++].RawValue = 0; + data[value].DataIndex = 2; data[value++].RawValue = 5; + status = HidP_SetData( HidP_Input, data, &value, preparsed_data, report, caps.InputReportByteLength ); + ok( status == HIDP_STATUS_IS_VALUE_ARRAY, "HidP_SetData returned %#lx\n", status ); + ok( value == 3, "got data count %ld, expected %d\n", value, 3 ); + ok( !memcmp( report, expect_report_0, caps.InputReportByteLength ), "report mismatch\n" ); + + value = 0; + data[value].DataIndex = 18; data[value++].RawValue = 1; + data[value].DataIndex = 19; data[value++].RawValue = 1; + data[value].DataIndex = 18; data[value++].RawValue = 0; + data[value].DataIndex = 18; data[value++].RawValue = 0; + data[value].DataIndex = 20; data[value++].RawValue = 1; + status = HidP_SetData( HidP_Input, data, &value, preparsed_data, report, caps.InputReportByteLength ); + ok( status == HIDP_STATUS_BUTTON_NOT_PRESSED, "HidP_SetData returned %#lx\n", status ); + ok( value == 3, "got data count %ld, expected %d\n", value, 3 ); + ok( !memcmp( report, expect_report_1, caps.InputReportByteLength ), "report mismatch\n" ); + + value = 0; + data[value].DataIndex = 2; data[value++].RawValue = 1; + data[value].DataIndex = 2; data[value++].RawValue = 1; + data[value].DataIndex = 18; data[value++].RawValue = 1; + data[value].DataIndex = 18; data[value++].RawValue = 1; + data[value].DataIndex = 20; data[value++].RawValue = 1; + status = HidP_SetData( HidP_Input, data, &value, preparsed_data, report, caps.InputReportByteLength ); + ok( status == HIDP_STATUS_BUFFER_TOO_SMALL, "HidP_SetData returned %#lx\n", status ); + ok( value == 3, "got data count %ld, expected %d\n", value, 3 ); + ok( !memcmp( report, expect_report_2, caps.InputReportByteLength ), "report mismatch\n" ); + + memset( report, 0, caps.InputReportByteLength ); + report[0] = report_id; + value = ARRAY_SIZE(expect_data); + memcpy( data, expect_data, sizeof(expect_data) ); + status = HidP_SetData( HidP_Input, data, &value, preparsed_data, report, caps.InputReportByteLength ); + ok( status == HIDP_STATUS_SUCCESS, "HidP_SetData returned %#lx\n", status ); + ok( value == ARRAY_SIZE(expect_data), "got data count %ld, expected %d\n", value, 5 ); + + value = ARRAY_SIZE(expect_data); + memset( data, 0, sizeof(data) ); + status = HidP_GetData( HidP_Input, data, &value, preparsed_data, report, caps.InputReportByteLength ); + ok( status == HIDP_STATUS_SUCCESS, "HidP_GetData returned %#lx\n", status ); + for (i = 0; i < ARRAY_SIZE(expect_data); ++i) + { + winetest_push_context( "data[%d]", i ); + check_member( data[i], expect_data[i], "%d", DataIndex ); + check_member( data[i], expect_data[i], "%ld", RawValue ); + winetest_pop_context(); + } + /* HID nary usage collections are set with 1-based usage index in their declaration order */
memset( report, 0, caps.InputReportByteLength ); diff --git a/dlls/hid/hid.spec b/dlls/hid/hid.spec index 67c4473129d..e64dfc29843 100644 --- a/dlls/hid/hid.spec +++ b/dlls/hid/hid.spec @@ -34,7 +34,7 @@ @ stdcall HidP_InitializeReportForID(long long ptr ptr long) @ stdcall HidP_MaxDataListLength(long ptr) @ stdcall HidP_MaxUsageListLength(long long ptr) -@ stub HidP_SetData +@ stdcall HidP_SetData(long ptr ptr ptr ptr long) @ 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) diff --git a/dlls/hid/hidp.c b/dlls/hid/hidp.c index c24a98155b0..27c2befe1b4 100644 --- a/dlls/hid/hidp.c +++ b/dlls/hid/hidp.c @@ -1029,3 +1029,93 @@ NTSTATUS WINAPI HidP_GetLinkCollectionNodes( HIDP_LINK_COLLECTION_NODE *nodes, U
return HIDP_STATUS_SUCCESS; } + +struct set_data_params +{ + HIDP_DATA *data; + char *report_buf; + BOOL found; +}; + +static NTSTATUS set_data( const struct hid_value_caps *caps, void *user ) +{ + ULONG index_min, index_max, index, lookup, bit, last, mask, bit_count; + const struct hid_value_caps *end = caps; + struct set_data_params *params = user; + HIDP_DATA *data = params->data; + unsigned char *report_buf; + + if (!caps->bit_size) return HIDP_STATUS_SUCCESS; + if (data->DataIndex < caps->data_index_min) return HIDP_STATUS_SUCCESS; + if (data->DataIndex > caps->data_index_max) return HIDP_STATUS_SUCCESS; + params->found = TRUE; + + report_buf = (unsigned char *)params->report_buf + caps->start_byte; + + if (HID_VALUE_CAPS_IS_ARRAY( caps )) + { + if (!(caps->flags & HID_VALUE_CAPS_IS_RANGE)) return HIDP_STATUS_IS_VALUE_ARRAY; + + while (end->flags & HID_VALUE_CAPS_ARRAY_HAS_MORE) end++; + index_min = end - caps + 1; + index_max = index_min + caps->usage_max - caps->usage_min; + lookup = index_min + data->DataIndex - caps->data_index_min; + + for (bit = caps->start_bit, last = bit + caps->report_count * caps->bit_size - 1; bit <= last; bit += 8) + { + if (!(index = report_buf[bit / 8]) || index < index_min || index > index_max) break; + if (!data->RawValue && index == lookup) break; + } + if (bit > last) return data->RawValue ? HIDP_STATUS_BUFFER_TOO_SMALL : HIDP_STATUS_BUTTON_NOT_PRESSED; + + if (data->RawValue) report_buf[bit / 8] = lookup; + else if (report_buf[bit / 8] != lookup) return HIDP_STATUS_BUTTON_NOT_PRESSED; + else + { + while (bit < last) { report_buf[bit / 8] = report_buf[bit / 8 + 1]; bit++; } + report_buf[bit / 8] = 0; + } + } + else if (caps->flags & HID_VALUE_CAPS_IS_BUTTON) + { + bit = caps->start_bit + (data->DataIndex - caps->data_index_min) * caps->bit_size, mask = 1 << (bit % 8); + + if (data->On) report_buf[bit / 8] |= mask; + else if (!(report_buf[bit / 8] & mask)) return HIDP_STATUS_BUTTON_NOT_PRESSED; + else report_buf[bit / 8] &= ~mask; + } + else if (caps->report_count == 1) + { + bit_count = caps->bit_size * caps->report_count; + copy_bits( report_buf, (void *)&data->RawValue, bit_count, -caps->start_bit ); + } + + return HIDP_STATUS_SUCCESS; +} + +NTSTATUS WINAPI HidP_SetData( HIDP_REPORT_TYPE report_type, HIDP_DATA *data, ULONG *data_len, + PHIDP_PREPARSED_DATA preparsed_data, char *report_buf, ULONG report_len ) +{ + struct hid_preparsed_data *preparsed = (struct hid_preparsed_data *)preparsed_data; + struct set_data_params params = {.report_buf = report_buf}; + HIDP_DATA *data_end = data + *data_len; + NTSTATUS status = HIDP_STATUS_SUCCESS; + + TRACE( "report_type %d, data %p, data_len %p, preparsed_data %p, report_buf %p, report_len %lu.\n", + report_type, data, data_len, preparsed_data, report_buf, report_len ); + + if (!report_len) return HIDP_STATUS_INVALID_REPORT_LENGTH; + + for (params.data = data; params.data < data_end; params.data++) + { + struct caps_filter filter = {.usage_page = USAGE_ANY, .usage = USAGE_ANY, .report_id = report_buf[0]}; + USHORT limit = -1; + + status = enum_value_caps( preparsed, report_type, report_len, &filter, set_data, ¶ms, &limit ); + if (status != HIDP_STATUS_SUCCESS || !params.found) break; + } + *data_len = params.data - data; + + if (!params.found) return HIDP_STATUS_REPORT_DOES_NOT_EXIST; + return status; +} diff --git a/dlls/hidparse.sys/hidparse.sys.spec b/dlls/hidparse.sys/hidparse.sys.spec index c723b51bed5..39b05ec7b93 100644 --- a/dlls/hidparse.sys/hidparse.sys.spec +++ b/dlls/hidparse.sys/hidparse.sys.spec @@ -18,7 +18,7 @@ @ stdcall HidP_InitializeReportForID(long long ptr ptr long) @ stdcall HidP_MaxDataListLength(long ptr) @ stdcall HidP_MaxUsageListLength(long long ptr) -@ stub HidP_SetData +@ stdcall HidP_SetData(long ptr ptr ptr ptr long) @ stub HidP_SetScaledUsageValue @ stdcall HidP_SetUsages(long long long ptr ptr ptr ptr long) @ stdcall HidP_SetUsageValue(long long long long long ptr ptr long) diff --git a/include/ddk/hidpi.h b/include/ddk/hidpi.h index e2e2bd2229b..29c3c3221fc 100644 --- a/include/ddk/hidpi.h +++ b/include/ddk/hidpi.h @@ -217,6 +217,7 @@ NTSTATUS WINAPI HidP_GetSpecificValueCaps(HIDP_REPORT_TYPE ReportType, USAGE Usa NTSTATUS WINAPI HidP_GetUsagesEx(HIDP_REPORT_TYPE ReportType, USHORT LinkCollection, USAGE_AND_PAGE *ButtonList, ULONG *UsageLength, PHIDP_PREPARSED_DATA PreparsedData, CHAR *Report, ULONG ReportLength); ULONG WINAPI HidP_MaxDataListLength(HIDP_REPORT_TYPE ReportType, PHIDP_PREPARSED_DATA PreparsedData); NTSTATUS WINAPI HidP_GetData(HIDP_REPORT_TYPE ReportType, HIDP_DATA *DataList, ULONG *DataLength, PHIDP_PREPARSED_DATA PreparsedData, CHAR *Report, ULONG ReportLength); +NTSTATUS WINAPI HidP_SetData(HIDP_REPORT_TYPE ReportType, HIDP_DATA *DataList, ULONG *DataLength, PHIDP_PREPARSED_DATA PreparsedData, CHAR *Report, ULONG ReportLength); NTSTATUS WINAPI HidP_GetLinkCollectionNodes(HIDP_LINK_COLLECTION_NODE *LinkCollectionNode, ULONG *LinkCollectionNodeLength, PHIDP_PREPARSED_DATA PreparsedData);
#ifndef FACILITY_HID_ERROR_CODE