Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
dlls/dinput8/tests/driver_hid.c | 125 ++++++++++++++++++++++++++------
dlls/dinput8/tests/driver_hid.h | 1 +
dlls/dinput8/tests/hid.c | 50 +++++++++++++
3 files changed, 155 insertions(+), 21 deletions(-)
diff --git a/dlls/dinput8/tests/driver_hid.c b/dlls/dinput8/tests/driver_hid.c
index 91f420d7bf6..cbaa1e89959 100644
--- a/dlls/dinput8/tests/driver_hid.c
+++ b/dlls/dinput8/tests/driver_hid.c
@@ -199,11 +199,97 @@ static void irp_queue_init( struct irp_queue *queue )
InitializeListHead( &queue->list );
}
+struct input_queue
+{
+ KSPIN_LOCK lock;
+ struct hid_expect *pos;
+ struct hid_expect *end;
+ struct hid_expect *buffer;
+ struct irp_queue pending;
+};
+
+static void input_queue_init( struct input_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;
+ irp_queue_init( &queue->pending );
+}
+
+static void input_queue_cleanup( struct input_queue *queue )
+{
+ ExFreePool( queue->buffer );
+}
+
+static BOOL input_queue_read_locked( struct input_queue *queue, IRP *irp )
+{
+ struct hid_expect *tmp = queue->pos;
+ if (tmp >= queue->end) return FALSE;
+
+ memcpy( irp->UserBuffer, tmp->report_buf, tmp->ret_length );
+ irp->IoStatus.Information = tmp->ret_length;
+ irp->IoStatus.Status = tmp->ret_status;
+ if (tmp < queue->end) queue->pos = tmp + 1;
+
+ /* loop on the queue data in polled mode */
+ if (polled && queue->pos == queue->end) queue->pos = queue->buffer;
+ return TRUE;
+}
+
+static NTSTATUS input_queue_read( struct input_queue *queue, IRP *irp )
+{
+ NTSTATUS status;
+ KIRQL irql;
+
+ KeAcquireSpinLock( &queue->lock, &irql );
+ if (input_queue_read_locked( queue, irp )) status = STATUS_SUCCESS;
+ else
+ {
+ IoMarkIrpPending( irp );
+ irp_queue_push( &queue->pending, irp );
+ status = STATUS_PENDING;
+ }
+ KeReleaseSpinLock( &queue->lock, irql );
+
+ return status;
+}
+
+static void input_queue_reset( struct input_queue *queue, void *in_buf, ULONG in_size )
+{
+ struct irp_queue completed;
+ ULONG remaining;
+ KIRQL irql;
+ IRP *irp;
+
+ irp_queue_init( &completed );
+
+ KeAcquireSpinLock( &queue->lock, &irql );
+ remaining = queue->end - queue->pos;
+ queue->pos = queue->buffer;
+ queue->end = queue->buffer;
+ memcpy( queue->end, in_buf, in_size );
+ queue->end += in_size / sizeof(struct hid_expect);
+
+ while (!polled && queue->pos < queue->end && (irp = irp_queue_pop( &queue->pending )))
+ {
+ input_queue_read_locked( queue, irp );
+ irp_queue_push( &completed, irp );
+ }
+ KeReleaseSpinLock( &queue->lock, irql );
+
+ if (!polled) ok( !remaining, "unread input\n" );
+
+ while ((irp = irp_queue_pop( &completed ))) IoCompleteRequest( irp, IO_NO_INCREMENT );
+}
+
+static struct input_queue input_queue;
+
struct hid_device
{
BOOL removed;
KSPIN_LOCK lock;
- struct irp_queue irp_queue;
};
static NTSTATUS WINAPI driver_pnp( DEVICE_OBJECT *device, IRP *irp )
@@ -221,7 +307,6 @@ static NTSTATUS WINAPI driver_pnp( DEVICE_OBJECT *device, IRP *irp )
++got_start_device;
impl->removed = FALSE;
KeInitializeSpinLock( &impl->lock );
- irp_queue_init( &impl->irp_queue );
IoSetDeviceInterfaceState( &control_symlink, TRUE );
irp->IoStatus.Status = STATUS_SUCCESS;
break;
@@ -231,7 +316,7 @@ static NTSTATUS WINAPI driver_pnp( DEVICE_OBJECT *device, IRP *irp )
KeAcquireSpinLock( &impl->lock, &irql );
impl->removed = TRUE;
KeReleaseSpinLock( &impl->lock, irql );
- irp_queue_clear( &impl->irp_queue );
+ irp_queue_clear( &input_queue.pending );
irp->IoStatus.Status = STATUS_SUCCESS;
break;
@@ -247,7 +332,7 @@ static NTSTATUS WINAPI driver_pnp( DEVICE_OBJECT *device, IRP *irp )
break;
case IRP_MN_REMOVE_DEVICE:
- irp_queue_clear( &impl->irp_queue );
+ irp_queue_clear( &input_queue.pending );
IoSetDeviceInterfaceState( &control_symlink, FALSE );
irp->IoStatus.Status = STATUS_SUCCESS;
break;
@@ -275,10 +360,9 @@ static NTSTATUS WINAPI driver_internal_ioctl( DEVICE_OBJECT *device, IRP *irp )
HID_DEVICE_EXTENSION *ext = device->DeviceExtension;
struct hid_device *impl = ext->MiniDeviceExtension;
const ULONG in_size = stack->Parameters.DeviceIoControl.InputBufferLength;
- const ULONG out_size = stack->Parameters.DeviceIoControl.OutputBufferLength;
+ 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;
@@ -358,21 +442,7 @@ static NTSTATUS WINAPI driver_internal_ioctl( DEVICE_OBJECT *device, IRP *irp )
ULONG expected_size = caps.InputReportByteLength - (report_id ? 0 : 1);
ok( !in_size, "got input size %u\n", in_size );
ok( out_size == expected_size, "got output size %u\n", out_size );
-
- if (polled)
- {
- memset( irp->UserBuffer, 0xa5, expected_size );
- if (report_id) ((char *)irp->UserBuffer)[0] = report_id;
- ((char *)irp->UserBuffer)[1] = seq++;
- irp->IoStatus.Information = 3;
- ret = STATUS_SUCCESS;
- }
- else
- {
- IoMarkIrpPending( irp );
- irp_queue_push( &impl->irp_queue, irp );
- ret = STATUS_PENDING;
- }
+ ret = input_queue_read( &input_queue, irp );
break;
}
@@ -535,6 +605,11 @@ static NTSTATUS WINAPI driver_ioctl( DEVICE_OBJECT *device, IRP *irp )
irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest( irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
+ case IOCTL_WINETEST_HID_SEND_INPUT:
+ input_queue_reset( &input_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 );
@@ -577,6 +652,7 @@ static NTSTATUS WINAPI driver_close( DEVICE_OBJECT *device, IRP *irp )
static void WINAPI driver_unload( DRIVER_OBJECT *driver )
{
+ input_queue_cleanup( &input_queue );
expect_queue_cleanup( &expect_queue );
winetest_cleanup();
}
@@ -647,6 +723,13 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *registry )
ok( !ret, "ZwQueryValueKey returned %#x\n", ret );
expect_queue_reset( &expect_queue, buffer + info_size, size - info_size );
+ input_queue_init( &input_queue );
+ RtlInitUnicodeString( &name_str, L"Input" );
+ size = info_size + EXPECT_QUEUE_BUFFER_SIZE;
+ ret = ZwQueryValueKey( hkey, &name_str, KeyValuePartialInformation, buffer, size, &size );
+ ok( !ret, "ZwQueryValueKey returned %#x\n", ret );
+ input_queue_reset( &input_queue, buffer + info_size, size - info_size );
+
driver->DriverExtension->AddDevice = driver_add_device;
driver->DriverUnload = driver_unload;
driver->MajorFunction[IRP_MJ_PNP] = driver_pnp;
diff --git a/dlls/dinput8/tests/driver_hid.h b/dlls/dinput8/tests/driver_hid.h
index c9c75c6d081..538d6205ab9 100644
--- a/dlls/dinput8/tests/driver_hid.h
+++ b/dlls/dinput8/tests/driver_hid.h
@@ -40,6 +40,7 @@
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)
+#define IOCTL_WINETEST_HID_SEND_INPUT CTL_CODE(FILE_DEVICE_KEYBOARD, 0x801, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
struct hid_expect
{
diff --git a/dlls/dinput8/tests/hid.c b/dlls/dinput8/tests/hid.c
index 85ccc2a05df..0623ad9ae50 100644
--- a/dlls/dinput8/tests/hid.c
+++ b/dlls/dinput8/tests/hid.c
@@ -743,6 +743,25 @@ static void set_hid_expect_( int line, HANDLE file, struct hid_expect *expect, D
ok( ret, "IOCTL_WINETEST_HID_SET_EXPECT failed, last error %u\n", GetLastError() );
}
+#define send_hid_input( a, b, c ) send_hid_input_( __LINE__, a, b, c )
+static void send_hid_input_( 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_SEND_INPUT, expect, expect_size, NULL, 0 );
+ ok( ret, "IOCTL_WINETEST_HID_SEND_INPUT failed, last error %u\n", GetLastError() );
+}
+
static void test_hidp_get_input( HANDLE file, int report_id, ULONG report_len, PHIDP_PREPARSED_DATA preparsed )
{
struct hid_expect expect[] =
@@ -2049,6 +2068,26 @@ static void test_hidp( HANDLE file, HANDLE async_file, int report_id, BOOL polle
if (polled)
{
+ struct hid_expect expect[] =
+ {
+ {
+ .code = IOCTL_HID_READ_REPORT,
+ .report_len = caps.InputReportByteLength - (report_id ? 0 : 1),
+ .report_buf = {report_id ? report_id : 0x5a,0x5a,0},
+ .ret_length = 3,
+ .ret_status = STATUS_SUCCESS,
+ },
+ {
+ .code = IOCTL_HID_READ_REPORT,
+ .report_len = caps.InputReportByteLength - (report_id ? 0 : 1),
+ .report_buf = {report_id ? report_id : 0x5a,0x5a,1},
+ .ret_length = 3,
+ .ret_status = STATUS_SUCCESS,
+ },
+ };
+
+ send_hid_input( file, expect, sizeof(expect) );
+
memset( report, 0xcd, sizeof(report) );
SetLastError( 0xdeadbeef );
ret = ReadFile( file, report, caps.InputReportByteLength, &value, NULL );
@@ -2541,6 +2580,14 @@ static void test_hid_driver( DWORD report_id, DWORD polled )
.NumberFeatureValueCaps = 6,
.NumberFeatureDataIndices = 8,
};
+ const struct hid_expect expect_in =
+ {
+ .code = IOCTL_HID_READ_REPORT,
+ .report_len = caps.InputReportByteLength - (report_id ? 0 : 1),
+ .report_buf = {report_id ? report_id : 0x5a,0x5a,0x5a},
+ .ret_length = 3,
+ .ret_status = STATUS_SUCCESS,
+ };
WCHAR cwd[MAX_PATH], tempdir[MAX_PATH];
LSTATUS status;
@@ -2572,6 +2619,9 @@ static void test_hid_driver( DWORD report_id, DWORD polled )
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, (void *)&expect_in, polled ? sizeof(expect_in) : 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();
--
2.33.0