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, ¶ms, &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 115032dbb52..82d04fa1bec 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,