From 334c6cf8f4222bb2737cd1f35588c95acc1737ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 25 Jun 2021 10:06:48 +0200 Subject: [PATCH] hid: Introduce new copy_bits helper for HidP_SetUsageValueArray. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RĂ©mi Bernon Signed-off-by: Alexandre Julliard --- dlls/hid/hidp.c | 70 ++++++++++++++++++++++++++++-- dlls/ntoskrnl.exe/tests/ntoskrnl.c | 6 +-- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/dlls/hid/hidp.c b/dlls/hid/hidp.c index 8d893f6327d..827de20c634 100644 --- a/dlls/hid/hidp.c +++ b/dlls/hid/hidp.c @@ -70,6 +70,7 @@ struct caps_filter { BOOLEAN buttons; BOOLEAN values; + BOOLEAN array; USAGE usage_page; USHORT collection; USAGE usage; @@ -102,6 +103,7 @@ static NTSTATUS enum_value_caps( WINE_HIDP_PREPARSED_DATA *preparsed, HIDP_REPOR { if (!match_value_caps( caps, filter )) continue; if (filter->report_id && caps->report_id != filter->report_id) continue; + else if (filter->array && (caps->is_range || caps->report_count <= 1)) return HIDP_STATUS_NOT_VALUE_ARRAY; else if (remaining-- > 0) status = callback( caps, user ); } @@ -114,6 +116,44 @@ static NTSTATUS enum_value_caps( WINE_HIDP_PREPARSED_DATA *preparsed, HIDP_REPOR return HIDP_STATUS_SUCCESS; } +/* copy count bits from src, starting at (-shift) bit if < 0, to dst starting at (shift) bit if > 0 */ +static void copy_bits( unsigned char *dst, const unsigned char *src, int count, int shift ) +{ + unsigned char bits, mask; + size_t src_shift = shift < 0 ? (-shift & 7) : 0; + size_t dst_shift = shift > 0 ? (shift & 7) : 0; + if (shift < 0) src += -shift / 8; + if (shift > 0) dst += shift / 8; + + if (src_shift == 0 && dst_shift == 0) + { + memcpy( dst, src, count / 8 ); + dst += count / 8; + src += count / 8; + count &= 7; + } + + if (!count) return; + + bits = *dst << (8 - dst_shift); + count += dst_shift; + + while (count > 8) + { + *dst = bits >> (8 - dst_shift); + bits = *(unsigned short *)src++ >> src_shift; + *dst++ |= bits << dst_shift; + count -= 8; + } + + bits >>= (8 - dst_shift); + if (count <= 8 - src_shift) bits |= (*src >> src_shift) << dst_shift; + else bits |= (*(unsigned short *)src >> src_shift) << dst_shift; + + mask = (1 << count) - 1; + *dst = (bits & mask) | (*dst & ~mask); +} + static NTSTATUS get_report_data(BYTE *report, INT reportLength, INT startBit, INT valueSize, PULONG value) { @@ -323,6 +363,13 @@ static NTSTATUS find_usage(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT return HIDP_STATUS_USAGE_NOT_FOUND; } +struct usage_value_params +{ + void *value_buf; + USHORT value_len; + void *report_buf; +}; + static LONG sign_extend(ULONG value, const WINE_HID_ELEMENT *element) { UINT bit_count = element->bitCount; @@ -514,6 +561,15 @@ ULONG WINAPI HidP_MaxUsageListLength( HIDP_REPORT_TYPE report_type, USAGE usage_ return count; } +static NTSTATUS set_usage_value( const struct hid_value_caps *caps, void *user ) +{ + struct usage_value_params *params = user; + ULONG bit_count = caps->bit_size * caps->report_count; + if ((bit_count + 7) / 8 > params->value_len) return HIDP_STATUS_BUFFER_TOO_SMALL; + copy_bits( params->report_buf, params->value_buf, bit_count, caps->start_bit ); + return HIDP_STATUS_NULL; +} + NTSTATUS WINAPI HidP_SetUsageValue(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection, USAGE Usage, ULONG UsageValue, PHIDP_PREPARSED_DATA PreparsedData, CHAR *Report, ULONG ReportLength) @@ -539,11 +595,19 @@ NTSTATUS WINAPI HidP_SetUsageValueArray( HIDP_REPORT_TYPE report_type, USAGE usa USAGE usage, char *value_buf, USHORT value_len, PHIDP_PREPARSED_DATA preparsed_data, char *report_buf, ULONG report_len ) { - FIXME( "report_type %d, usage_page %x, collection %d, usage %x, value_buf %p, value_len %u, " - "preparsed_data %p, report_buf %p, report_len %u stub!\n", + struct usage_value_params params = {.value_buf = value_buf, .value_len = value_len, .report_buf = report_buf}; + WINE_HIDP_PREPARSED_DATA *preparsed = (WINE_HIDP_PREPARSED_DATA *)preparsed_data; + struct caps_filter filter = {.values = TRUE, .array = TRUE, .usage_page = usage_page, .collection = collection, .usage = usage}; + USHORT count = 1; + + TRACE( "report_type %d, usage_page %x, collection %d, usage %x, value_buf %p, value_len %u, " + "preparsed_data %p, report_buf %p, report_len %u.\n", report_type, usage_page, collection, usage, value_buf, value_len, preparsed_data, report_buf, report_len ); - return HIDP_STATUS_NOT_IMPLEMENTED; + 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_usage_value, ¶ms, &count ); } struct set_usage_params diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index ca030599299..3185b0f7893 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -2021,15 +2021,14 @@ static void test_hidp(HANDLE file, int report_id) status = HidP_SetUsageValueArray(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_X, buffer, sizeof(buffer), preparsed_data, report, caps.InputReportByteLength); - todo_wine ok(status == HIDP_STATUS_NOT_VALUE_ARRAY, "HidP_SetUsageValueArray returned %#x\n", status); memset(buffer, 0xcd, sizeof(buffer)); status = HidP_SetUsageValueArray(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_HATSWITCH, buffer, 0, preparsed_data, report, caps.InputReportByteLength); - todo_wine ok(status == HIDP_STATUS_BUFFER_TOO_SMALL, "HidP_SetUsageValueArray returned %#x\n", status); status = HidP_SetUsageValueArray(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_HATSWITCH, buffer, 8, preparsed_data, report, caps.InputReportByteLength); + todo_wine ok(status == HIDP_STATUS_NOT_IMPLEMENTED, "HidP_SetUsageValueArray returned %#x\n", status); status = HidP_GetUsageValueArray(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_X, buffer, @@ -2365,13 +2364,10 @@ static void test_hidp(HANDLE file, int report_id) memset(buffer, 0xff, sizeof(buffer)); status = HidP_SetUsageValueArray(HidP_Feature, HID_USAGE_PAGE_HAPTICS, 0, HID_USAGE_HAPTICS_WAVEFORM_CUTOFF_TIME, buffer, 0, preparsed_data, report, caps.FeatureReportByteLength); - todo_wine ok(status == HIDP_STATUS_BUFFER_TOO_SMALL, "HidP_SetUsageValueArray returned %#x\n", status); status = HidP_SetUsageValueArray(HidP_Feature, HID_USAGE_PAGE_HAPTICS, 0, HID_USAGE_HAPTICS_WAVEFORM_CUTOFF_TIME, buffer, 64, preparsed_data, report, caps.FeatureReportByteLength); - todo_wine ok(status == HIDP_STATUS_SUCCESS, "HidP_SetUsageValueArray returned %#x\n", status); - todo_wine ok(!memcmp(report + 9, buffer, 8), "unexpected report data\n"); memset(buffer, 0, sizeof(buffer));