From af6971b5d06d8247b70f3667650abd7730938202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 13 Sep 2021 10:48:53 +0200 Subject: [PATCH] dinput8/tests: Control expected SET_FEATURE reports from the test executable. 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/dinput8/tests/driver_hid.c | 165 +++++++++++++++++++++++++++++-- dlls/dinput8/tests/driver_hid.h | 15 +++ dlls/dinput8/tests/hid.c | 170 +++++++++++++++++++++++--------- 3 files changed, 298 insertions(+), 52 deletions(-) diff --git a/dlls/dinput8/tests/driver_hid.c b/dlls/dinput8/tests/driver_hid.c index b393ddb41cb..9256bb4922e 100644 --- a/dlls/dinput8/tests/driver_hid.c +++ b/dlls/dinput8/tests/driver_hid.c @@ -46,6 +46,114 @@ static HIDP_CAPS caps; static DWORD report_id; static DWORD polled; +#define EXPECT_QUEUE_BUFFER_SIZE (64 * sizeof(struct hid_expect)) + +struct expect_queue +{ + KSPIN_LOCK lock; + struct hid_expect *pos; + struct hid_expect *end; + struct hid_expect spurious; + struct hid_expect *buffer; +}; + +static void expect_queue_init( struct expect_queue *queue ) +{ + KeInitializeSpinLock( &queue->lock ); + queue->buffer = ExAllocatePool( PagedPool, EXPECT_QUEUE_BUFFER_SIZE ); + RtlSecureZeroMemory( queue->buffer, EXPECT_QUEUE_BUFFER_SIZE ); + queue->pos = queue->buffer; + queue->end = queue->buffer; +} + +static void expect_queue_cleanup( struct expect_queue *queue ) +{ + ExFreePool( queue->buffer ); +} + +static void expect_queue_reset( struct expect_queue *queue, void *buffer, unsigned int size ) +{ + struct hid_expect *missing, *missing_end, *tmp; + KIRQL irql; + + missing = ExAllocatePool( PagedPool, EXPECT_QUEUE_BUFFER_SIZE ); + RtlSecureZeroMemory( missing, EXPECT_QUEUE_BUFFER_SIZE ); + missing_end = missing; + + KeAcquireSpinLock( &queue->lock, &irql ); + tmp = queue->pos; + while (tmp < queue->end) *missing_end++ = *tmp++; + + queue->pos = queue->buffer; + queue->end = queue->buffer; + + if (size) memcpy( queue->end, buffer, size ); + queue->end = queue->end + size / sizeof(struct hid_expect); + KeReleaseSpinLock( &queue->lock, irql ); + + tmp = missing; + while (tmp != missing_end) + { + winetest_push_context( "%s expect[%d]", tmp->context, tmp - missing ); + if (tmp->broken) + win_skip( "broken (code %#x id %u len %u)\n", tmp->code, tmp->report_id, tmp->report_len ); + else + ok( 0, "missing (code %#x id %u len %u)\n", tmp->code, tmp->report_id, tmp->report_len ); + winetest_pop_context(); + tmp++; + } + + ExFreePool( missing ); +} + +static void expect_queue_next( struct expect_queue *queue, ULONG code, HID_XFER_PACKET *packet, + LONG *index, struct hid_expect *expect ) +{ + struct hid_expect *missing, *missing_end, *tmp; + ULONG len = packet->reportBufferLen; + BYTE *buf = packet->reportBuffer; + BYTE id = packet->reportId; + KIRQL irql; + + missing = ExAllocatePool( PagedPool, EXPECT_QUEUE_BUFFER_SIZE ); + RtlSecureZeroMemory( missing, EXPECT_QUEUE_BUFFER_SIZE ); + missing_end = missing; + + KeAcquireSpinLock( &queue->lock, &irql ); + tmp = queue->pos; + while (tmp < queue->end) + { + if (!tmp->broken || running_under_wine) break; + if (tmp->code == code && tmp->report_id == id && tmp->report_len == len && + RtlCompareMemory( tmp->report_buf, buf, len ) == len) + break; + *missing_end++ = *tmp++; + } + *index = tmp - queue->buffer; + if (tmp < queue->end) queue->pos = tmp + 1; + else tmp = &queue->spurious; + *expect = *tmp; + KeReleaseSpinLock( &queue->lock, irql ); + + ok( tmp != &queue->spurious, "got spurious packet\n" ); + + tmp = missing; + while (tmp != missing_end) + { + winetest_push_context( "%s expect[%d]", tmp->context, tmp - missing ); + if (tmp->broken) + win_skip( "broken (code %#x id %u len %u)\n", tmp->code, tmp->report_id, tmp->report_len ); + else + ok( 0, "missing (code %#x id %u len %u)\n", tmp->code, tmp->report_id, tmp->report_len ); + winetest_pop_context(); + tmp++; + } + + ExFreePool( missing ); +} + +static struct expect_queue expect_queue; + struct irp_queue { KSPIN_LOCK lock; @@ -169,10 +277,12 @@ static NTSTATUS WINAPI driver_internal_ioctl( DEVICE_OBJECT *device, IRP *irp ) const ULONG in_size = stack->Parameters.DeviceIoControl.InputBufferLength; const ULONG out_size = stack->Parameters.DeviceIoControl.OutputBufferLength; const ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; + struct hid_expect expect = {0}; static BYTE seq = 0; NTSTATUS ret; BOOL removed; KIRQL irql; + LONG index; if (winetest_debug > 1) trace( "ioctl %#x\n", code ); @@ -191,6 +301,8 @@ static NTSTATUS WINAPI driver_internal_ioctl( DEVICE_OBJECT *device, IRP *irp ) return STATUS_DELETE_PENDING; } + winetest_push_context( "id %d%s", report_id, polled ? " poll" : "" ); + switch (code) { case IOCTL_HID_GET_DEVICE_DESCRIPTOR: @@ -345,8 +457,18 @@ static NTSTATUS WINAPI driver_internal_ioctl( DEVICE_OBJECT *device, IRP *irp ) ok( packet->reportBufferLen >= expected_size, "got len %u\n", packet->reportBufferLen ); ok( !!packet->reportBuffer, "got buffer %p\n", packet->reportBuffer ); - irp->IoStatus.Information = 3; - ret = STATUS_SUCCESS; + expect_queue_next( &expect_queue, code, packet, &index, &expect ); + winetest_push_context( "%s expect[%d]", expect.context, index ); + ok( expect.code == code, "got %#x, expected %#x\n", expect.code, code ); + ok( packet->reportId == expect.report_id, "got id %u\n", packet->reportId ); + todo_wine_if( expect.todo_report_len ) + ok( packet->reportBufferLen == expect.report_len, "got len %u\n", packet->reportBufferLen ); + ok( RtlCompareMemory( packet->reportBuffer, expect.report_buf, expect.report_len ) == expect.report_len, + "unexpected data\n" ); + winetest_pop_context(); + + irp->IoStatus.Information = expect.ret_length; + ret = expect.ret_status; break; } @@ -364,6 +486,8 @@ static NTSTATUS WINAPI driver_internal_ioctl( DEVICE_OBJECT *device, IRP *irp ) ret = STATUS_NOT_IMPLEMENTED; } + winetest_pop_context(); + if (ret != STATUS_PENDING) { irp->IoStatus.Status = ret; @@ -372,13 +496,23 @@ static NTSTATUS WINAPI driver_internal_ioctl( DEVICE_OBJECT *device, IRP *irp ) return ret; } +static NTSTATUS( WINAPI *hidclass_driver_ioctl )(DEVICE_OBJECT *device, IRP *irp); static NTSTATUS WINAPI driver_ioctl( DEVICE_OBJECT *device, IRP *irp ) { - HID_DEVICE_EXTENSION *ext = device->DeviceExtension; + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + ULONG in_size = stack->Parameters.DeviceIoControl.InputBufferLength; + ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; - ok( 0, "unexpected call\n" ); - IoSkipCurrentIrpStackLocation( irp ); - return IoCallDriver( ext->NextDeviceObject, irp ); + switch (code) + { + case IOCTL_WINETEST_HID_SET_EXPECT: + expect_queue_reset( &expect_queue, irp->AssociatedIrp.SystemBuffer, in_size ); + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return STATUS_SUCCESS; + } + + return hidclass_driver_ioctl( device, irp ); } static NTSTATUS WINAPI driver_add_device( DRIVER_OBJECT *driver, DEVICE_OBJECT *fdo ) @@ -418,13 +552,13 @@ static NTSTATUS WINAPI driver_close( DEVICE_OBJECT *device, IRP *irp ) static void WINAPI driver_unload( DRIVER_OBJECT *driver ) { + expect_queue_cleanup( &expect_queue ); winetest_cleanup(); } NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *registry ) { static const int info_size = offsetof( KEY_VALUE_PARTIAL_INFORMATION, Data ); - char buffer[offsetof( KEY_VALUE_PARTIAL_INFORMATION, Data ) + sizeof(report_descriptor_buf)]; HID_MINIDRIVER_REGISTRATION params = { .Revision = HID_REVISION, @@ -435,6 +569,7 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *registry ) UNICODE_STRING name_str; OBJECT_ATTRIBUTES attr; NTSTATUS ret; + char *buffer; HANDLE hkey; DWORD size; @@ -444,6 +579,10 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *registry ) ret = ZwOpenKey( &hkey, KEY_ALL_ACCESS, &attr ); ok( !ret, "ZwOpenKey returned %#x\n", ret ); + buffer = ExAllocatePool( PagedPool, info_size + EXPECT_QUEUE_BUFFER_SIZE ); + ok( buffer != NULL, "ExAllocatePool failed\n" ); + if (!buffer) return STATUS_NO_MEMORY; + RtlInitUnicodeString( &name_str, L"ReportID" ); size = info_size + sizeof(report_id); ret = ZwQueryValueKey( hkey, &name_str, KeyValuePartialInformation, buffer, size, &size ); @@ -476,6 +615,13 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *registry ) ok( !ret, "ZwQueryValueKey returned %#x\n", ret ); memcpy( &caps, buffer + info_size, size - info_size ); + expect_queue_init( &expect_queue ); + RtlInitUnicodeString( &name_str, L"Expect" ); + size = info_size + EXPECT_QUEUE_BUFFER_SIZE; + ret = ZwQueryValueKey( hkey, &name_str, KeyValuePartialInformation, buffer, size, &size ); + ok( !ret, "ZwQueryValueKey returned %#x\n", ret ); + expect_queue_reset( &expect_queue, buffer + info_size, size - info_size ); + driver->DriverExtension->AddDevice = driver_add_device; driver->DriverUnload = driver_unload; driver->MajorFunction[IRP_MJ_PNP] = driver_pnp; @@ -485,8 +631,13 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *registry ) driver->MajorFunction[IRP_MJ_CREATE] = driver_create; driver->MajorFunction[IRP_MJ_CLOSE] = driver_close; + ExFreePool( buffer ); + ret = HidRegisterMinidriver( ¶ms ); ok( !ret, "got %#x\n", ret ); + hidclass_driver_ioctl = driver->MajorFunction[IRP_MJ_DEVICE_CONTROL]; + driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver_ioctl; + return STATUS_SUCCESS; } diff --git a/dlls/dinput8/tests/driver_hid.h b/dlls/dinput8/tests/driver_hid.h index 42d12a0cf27..e7202594c25 100644 --- a/dlls/dinput8/tests/driver_hid.h +++ b/dlls/dinput8/tests/driver_hid.h @@ -39,6 +39,21 @@ DEFINE_GUID(control_class,0xdeadbeef,0x29ef,0x4538,0xa5,0xfd,0xb6,0x95,0x73,0xa3,0x62,0xc0); +#define IOCTL_WINETEST_HID_SET_EXPECT CTL_CODE(FILE_DEVICE_KEYBOARD, 0x800, METHOD_IN_DIRECT, FILE_ANY_ACCESS) + +struct hid_expect +{ + DWORD code; + DWORD ret_length; + DWORD ret_status; + BYTE broken; /* missing on some win versions */ + BYTE report_id; + BYTE todo_report_len; + BYTE report_len; + BYTE report_buf[128]; + char context[64]; +}; + /* kernel/user shared data */ struct winetest_shared_data { diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c index 4483a5ec479..4f60c9f2194 100644 --- a/dlls/dinput8/tests/hid.c +++ b/dlls/dinput8/tests/hid.c @@ -724,6 +724,127 @@ static BOOL sync_ioctl( HANDLE file, DWORD code, void *in_buf, DWORD in_len, voi return ret; } +#define set_hid_expect( a, b, c ) set_hid_expect_( __LINE__, a, b, c ) +static void set_hid_expect_( int line, HANDLE file, struct hid_expect *expect, DWORD expect_size ) +{ + const char *source_file; + BOOL ret; + int i; + + source_file = strrchr( __FILE__, '/' ); + if (!source_file) source_file = strrchr( __FILE__, '\\' ); + if (!source_file) source_file = __FILE__; + else source_file++; + + for (i = 0; i < expect_size / sizeof(struct hid_expect); ++i) + snprintf( expect[i].context, ARRAY_SIZE(expect[i].context), "%s:%d", source_file, line ); + + ret = sync_ioctl( file, IOCTL_WINETEST_HID_SET_EXPECT, expect, expect_size, NULL, 0 ); + ok( ret, "IOCTL_WINETEST_HID_SET_EXPECT failed, last error %u\n", GetLastError() ); +} + +static void test_hidp_set_feature( HANDLE file, int report_id, ULONG report_len, PHIDP_PREPARSED_DATA preparsed ) +{ + struct hid_expect expect[] = + { + { + .code = IOCTL_HID_SET_FEATURE, + .report_id = report_id, + .report_len = report_len - (report_id ? 0 : 1), + .report_buf = {report_id}, + .ret_length = 3, + .ret_status = STATUS_SUCCESS, + }, + { + .code = IOCTL_HID_SET_FEATURE, + .report_id = report_id, + .todo_report_len = TRUE, + .report_len = report_len - (report_id ? 0 : 1), + .report_buf = + { + report_id,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd, + 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd, + }, + .ret_length = 3, + .ret_status = STATUS_SUCCESS, + }, + }; + char buffer[200], report[200]; + NTSTATUS status; + ULONG length; + BOOL ret; + + memset( report, 0xcd, sizeof(report) ); + status = HidP_InitializeReportForID( HidP_Feature, report_id, preparsed, report, report_len ); + ok( status == HIDP_STATUS_SUCCESS, "HidP_InitializeReportForID returned %#x\n", status ); + + SetLastError( 0xdeadbeef ); + ret = HidD_SetFeature( file, report, 0 ); + ok( !ret, "HidD_SetFeature succeeded\n" ); + ok( GetLastError() == ERROR_INVALID_USER_BUFFER, "HidD_SetFeature returned error %u\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + ret = HidD_SetFeature( file, report, report_len - 1 ); + ok( !ret, "HidD_SetFeature succeeded\n" ); + ok( GetLastError() == ERROR_INVALID_PARAMETER || broken( GetLastError() == ERROR_CRC ), + "HidD_SetFeature returned error %u\n", GetLastError() ); + + if (!report_id) + { + struct hid_expect broken_expect = + { + .code = IOCTL_HID_SET_FEATURE, + .broken = TRUE, + .report_len = report_len - 1, + .report_buf = + { + 0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a, + 0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a, + 0x5a,0x5a,0x5a,0x5a,0x5a, + }, + .ret_length = 3, + .ret_status = STATUS_SUCCESS, + }; + + set_hid_expect( file, &broken_expect, sizeof(broken_expect) ); + } + + SetLastError( 0xdeadbeef ); + memset( buffer, 0x5a, sizeof(buffer) ); + ret = HidD_SetFeature( file, buffer, report_len ); + if (report_id || broken( !ret )) + { + ok( !ret, "HidD_SetFeature succeeded, last error %u\n", GetLastError() ); + ok( GetLastError() == ERROR_INVALID_PARAMETER || broken( GetLastError() == ERROR_CRC ), + "HidD_SetFeature returned error %u\n", GetLastError() ); + } + else + { + ok( ret, "HidD_SetFeature failed, last error %u\n", GetLastError() ); + } + + set_hid_expect( file, expect, sizeof(expect) ); + + SetLastError( 0xdeadbeef ); + ret = HidD_SetFeature( file, report, report_len ); + ok( ret, "HidD_SetFeature failed, last error %u\n", GetLastError() ); + + length = report_len * 2; + SetLastError( 0xdeadbeef ); + ret = sync_ioctl( file, IOCTL_HID_SET_FEATURE, NULL, 0, report, &length ); + ok( !ret, "IOCTL_HID_SET_FEATURE succeeded\n" ); + ok( GetLastError() == ERROR_INVALID_USER_BUFFER, "IOCTL_HID_SET_FEATURE returned error %u\n", + GetLastError() ); + length = 0; + SetLastError( 0xdeadbeef ); + ret = sync_ioctl( file, IOCTL_HID_SET_FEATURE, report, report_len * 2, NULL, &length ); + ok( ret, "IOCTL_HID_SET_FEATURE failed, last error %u\n", GetLastError() ); + ok( length == 3, "got length %u, expected 3\n", length ); + + set_hid_expect( file, NULL, 0 ); +} + static void test_hidp( HANDLE file, HANDLE async_file, int report_id, BOOL polled, const HIDP_CAPS *expect_caps ) { const HIDP_BUTTON_CAPS expect_button_caps[] = @@ -1653,51 +1774,7 @@ static void test_hidp( HANDLE file, HANDLE async_file, int report_id, BOOL polle ok( value == 3, "got length %u, expected 3\n", value ); ok( report[0] == report_id, "got report[0] %02x, expected %02x\n", report[0], report_id ); - memset( report, 0xcd, sizeof(report) ); - status = HidP_InitializeReportForID( HidP_Feature, report_id, preparsed_data, report, - caps.FeatureReportByteLength ); - ok( status == HIDP_STATUS_SUCCESS, "HidP_InitializeReportForID returned %#x\n", status ); - - SetLastError( 0xdeadbeef ); - ret = HidD_SetFeature( file, report, 0 ); - ok( !ret, "HidD_SetFeature succeeded\n" ); - ok( GetLastError() == ERROR_INVALID_USER_BUFFER, "HidD_SetFeature returned error %u\n", GetLastError() ); - - SetLastError( 0xdeadbeef ); - ret = HidD_SetFeature( file, report, caps.FeatureReportByteLength - 1 ); - ok( !ret, "HidD_SetFeature succeeded\n" ); - ok( GetLastError() == ERROR_INVALID_PARAMETER || broken( GetLastError() == ERROR_CRC ), - "HidD_SetFeature returned error %u\n", GetLastError() ); - - SetLastError( 0xdeadbeef ); - memset( buffer, 0x5a, sizeof(buffer) ); - ret = HidD_SetFeature( file, buffer, caps.FeatureReportByteLength ); - if (report_id || broken( !ret )) - { - ok( !ret, "HidD_SetFeature succeeded, last error %u\n", GetLastError() ); - ok( GetLastError() == ERROR_INVALID_PARAMETER || broken( GetLastError() == ERROR_CRC ), - "HidD_SetFeature returned error %u\n", GetLastError() ); - } - else - { - ok( ret, "HidD_SetFeature failed, last error %u\n", GetLastError() ); - } - - SetLastError( 0xdeadbeef ); - ret = HidD_SetFeature( file, report, caps.FeatureReportByteLength ); - ok( ret, "HidD_SetFeature failed, last error %u\n", GetLastError() ); - - value = caps.FeatureReportByteLength * 2; - SetLastError( 0xdeadbeef ); - ret = sync_ioctl( file, IOCTL_HID_SET_FEATURE, NULL, 0, report, &value ); - ok( !ret, "IOCTL_HID_SET_FEATURE succeeded\n" ); - ok( GetLastError() == ERROR_INVALID_USER_BUFFER, "IOCTL_HID_SET_FEATURE returned error %u\n", - GetLastError() ); - value = 0; - SetLastError( 0xdeadbeef ); - ret = sync_ioctl( file, IOCTL_HID_SET_FEATURE, report, caps.FeatureReportByteLength * 2, NULL, &value ); - ok( ret, "IOCTL_HID_SET_FEATURE failed, last error %u\n", GetLastError() ); - ok( value == 3, "got length %u, expected 3\n", value ); + test_hidp_set_feature( file, report_id, caps.FeatureReportByteLength, preparsed_data ); memset( report, 0xcd, sizeof(report) ); status = HidP_InitializeReportForID( HidP_Output, report_id, preparsed_data, report, caps.OutputReportByteLength ); @@ -2318,6 +2395,9 @@ static void test_hid_driver( DWORD report_id, DWORD polled ) status = RegSetValueExW( hkey, L"Caps", 0, REG_BINARY, (void *)&caps, sizeof(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 ); + if (pnp_driver_start( L"driver_hid.dll" )) test_hid_device( report_id, polled, &caps ); pnp_driver_stop();