dinput8/tests: Control expected READ_REPORT reports from the test executable.

Signed-off-by: Rémi Bernon <rbernon@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Rémi Bernon 2021-09-14 10:38:38 +02:00 committed by Alexandre Julliard
parent 1eb56b20ba
commit 10ddee1cda
3 changed files with 155 additions and 21 deletions

View File

@ -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;

View File

@ -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
{

View File

@ -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();