Commit bd2b8980 authored by Rémi Bernon's avatar Rémi Bernon Committed by Alexandre Julliard

dinput/tests: Add a helper to wait on HID input reads.

parent a4d2e62a
......@@ -1738,6 +1738,7 @@ static void test_hid_mouse(void)
mouse_move_count = 0;
bus_send_hid_input( file, &desc, single_move, sizeof(single_move) );
bus_wait_hid_input( file, &desc, 100 );
res = MsgWaitForMultipleObjects( 0, NULL, FALSE, 500, QS_MOUSEMOVE );
todo_wine
......
......@@ -108,6 +108,11 @@ void wait_hid_expect_( const char *file, int line, HANDLE device, struct hid_dev
void send_hid_input_( const char *file, int line, HANDLE device, struct hid_device_desc *desc,
struct hid_expect *expect, DWORD expect_size );
#define wait_hid_input( a, b ) wait_hid_input_( __FILE__, __LINE__, a, NULL, b, FALSE )
#define bus_wait_hid_input( a, b, c ) wait_hid_input_( __FILE__, __LINE__, a, b, c, FALSE )
void wait_hid_input_( const char *file, int line, HANDLE device, struct hid_device_desc *desc,
DWORD timeout, BOOL todo );
#define msg_wait_for_events( a, b, c ) msg_wait_for_events_( __FILE__, __LINE__, a, b, c )
DWORD msg_wait_for_events_( const char *file, int line, DWORD count, HANDLE *events, DWORD timeout );
......
......@@ -63,27 +63,13 @@ static void check_buffer_( int line, HID_XFER_PACKET *packet, struct hid_expect
}
}
struct expect_queue
struct wait_queue
{
KSPIN_LOCK lock;
struct hid_expect *pos;
struct hid_expect *end;
struct hid_expect spurious;
struct hid_expect *buffer;
IRP *pending_wait;
char context[64];
};
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 )
static void wait_queue_cleanup( struct wait_queue *queue )
{
KIRQL irql;
IRP *irp;
......@@ -101,7 +87,50 @@ static void expect_queue_cleanup( struct expect_queue *queue )
irp->IoStatus.Status = STATUS_DELETE_PENDING;
IoCompleteRequest( irp, IO_NO_INCREMENT );
}
}
static void wait_queue_unlock( struct wait_queue *queue, KIRQL irql, BOOL empty )
{
IRP *irp;
/* complete the pending wait IRP if the queue is now empty */
if ((irp = empty ? queue->pending_wait : NULL))
{
queue->pending_wait = NULL;
if (!IoSetCancelRoutine( irp, NULL )) irp = NULL;
}
KeReleaseSpinLock( &queue->lock, irql );
if (irp)
{
irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest( irp, IO_NO_INCREMENT );
}
}
struct expect_queue
{
struct wait_queue base;
struct hid_expect *pos;
struct hid_expect *end;
struct hid_expect spurious;
struct hid_expect *buffer;
char context[64];
};
static void expect_queue_init( struct expect_queue *queue )
{
KeInitializeSpinLock( &queue->base.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 )
{
wait_queue_cleanup( &queue->base );
ExFreePool( queue->buffer );
}
......@@ -115,7 +144,7 @@ static void expect_queue_reset( struct expect_queue *queue, void *buffer, unsign
RtlSecureZeroMemory( missing, EXPECT_QUEUE_BUFFER_SIZE );
missing_end = missing;
KeAcquireSpinLock( &queue->lock, &irql );
KeAcquireSpinLock( &queue->base.lock, &irql );
tmp = queue->pos;
while (tmp < queue->end) *missing_end++ = *tmp++;
......@@ -125,7 +154,7 @@ static void expect_queue_reset( struct expect_queue *queue, void *buffer, unsign
if (size) memcpy( queue->end, buffer, size );
queue->end = queue->end + size / sizeof(struct hid_expect);
memcpy( context, queue->context, sizeof(context) );
KeReleaseSpinLock( &queue->lock, irql );
KeReleaseSpinLock( &queue->base.lock, irql );
tmp = missing;
while (tmp != missing_end)
......@@ -150,7 +179,7 @@ static void expect_queue_reset( struct expect_queue *queue, void *buffer, unsign
static void WINAPI wait_cancel_routine( DEVICE_OBJECT *device, IRP *irp )
{
struct expect_queue *queue = irp->Tail.Overlay.DriverContext[0];
struct wait_queue *queue = irp->Tail.Overlay.DriverContext[0];
KIRQL irql;
IoReleaseCancelSpinLock( irp->CancelIrql );
......@@ -164,7 +193,7 @@ static void WINAPI wait_cancel_routine( DEVICE_OBJECT *device, IRP *irp )
IoCompleteRequest( irp, IO_NO_INCREMENT );
}
static NTSTATUS expect_queue_add_pending_locked( struct expect_queue *queue, IRP *irp )
static NTSTATUS wait_queue_add_pending_locked( struct wait_queue *queue, IRP *irp )
{
if (queue->pending_wait) return STATUS_INVALID_PARAMETER;
......@@ -184,9 +213,9 @@ static NTSTATUS expect_queue_add_pending( struct expect_queue *queue, IRP *irp )
NTSTATUS status;
KIRQL irql;
KeAcquireSpinLock( &queue->lock, &irql );
status = expect_queue_add_pending_locked( queue, irp );
KeReleaseSpinLock( &queue->lock, irql );
KeAcquireSpinLock( &queue->base.lock, &irql );
status = wait_queue_add_pending_locked( &queue->base, irp );
KeReleaseSpinLock( &queue->base.lock, irql );
return status;
}
......@@ -198,16 +227,16 @@ static NTSTATUS expect_queue_wait_pending( struct expect_queue *queue, IRP *irp
IRP *pending;
KIRQL irql;
KeAcquireSpinLock( &queue->lock, &irql );
if ((pending = queue->pending_wait))
KeAcquireSpinLock( &queue->base.lock, &irql );
if ((pending = queue->base.pending_wait))
{
queue->pending_wait = NULL;
queue->base.pending_wait = NULL;
if (!IoSetCancelRoutine( pending, NULL )) pending = NULL;
}
if (pending && queue->pos == queue->end) status = STATUS_SUCCESS;
else status = expect_queue_add_pending_locked( queue, irp );
KeReleaseSpinLock( &queue->lock, irql );
else status = wait_queue_add_pending_locked( &queue->base, irp );
KeReleaseSpinLock( &queue->base.lock, irql );
if (pending)
{
......@@ -225,10 +254,10 @@ static NTSTATUS expect_queue_wait( struct expect_queue *queue, IRP *irp )
KIRQL irql;
irp->IoStatus.Information = 0;
KeAcquireSpinLock( &queue->lock, &irql );
KeAcquireSpinLock( &queue->base.lock, &irql );
if (queue->pos == queue->end) status = STATUS_SUCCESS;
else status = expect_queue_add_pending_locked( queue, irp );
KeReleaseSpinLock( &queue->lock, irql );
else status = wait_queue_add_pending_locked( &queue->base, irp );
KeReleaseSpinLock( &queue->base.lock, irql );
return status;
}
......@@ -240,14 +269,13 @@ static void expect_queue_next( struct expect_queue *queue, ULONG code, HID_XFER_
ULONG len = packet->reportBufferLen;
BYTE *buf = packet->reportBuffer;
BYTE id = packet->reportId;
IRP *irp = NULL;
KIRQL irql;
missing = ExAllocatePool( PagedPool, EXPECT_QUEUE_BUFFER_SIZE );
RtlSecureZeroMemory( missing, EXPECT_QUEUE_BUFFER_SIZE );
missing_end = missing;
KeAcquireSpinLock( &queue->lock, &irql );
KeAcquireSpinLock( &queue->base.lock, &irql );
tmp = queue->pos;
while (tmp < queue->end)
{
......@@ -270,28 +298,12 @@ static void expect_queue_next( struct expect_queue *queue, ULONG code, HID_XFER_
queue->pos++;
}
if ((irp = queue->pending_wait))
{
/* don't mark the IRP as pending if someone's already waiting */
if (expect->ret_status == STATUS_PENDING) expect->ret_status = STATUS_SUCCESS;
/* complete the pending wait IRP if the queue is now empty */
if (queue->pos != queue->end) irp = NULL;
else
{
queue->pending_wait = NULL;
if (!IoSetCancelRoutine( irp, NULL )) irp = NULL;
}
}
/* don't mark the IRP as pending if someone's already waiting */
if (expect->ret_status == STATUS_PENDING && queue->base.pending_wait)
expect->ret_status = STATUS_SUCCESS;
memcpy( context, queue->context, context_size );
KeReleaseSpinLock( &queue->lock, irql );
if (irp)
{
irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest( irp, IO_NO_INCREMENT );
}
wait_queue_unlock( &queue->base, irql, queue->pos == queue->end );
ok( tmp != &queue->spurious, "%s got spurious packet\n", context );
......@@ -368,7 +380,7 @@ static void irp_queue_init( struct irp_queue *queue )
struct input_queue
{
KSPIN_LOCK lock;
struct wait_queue base;
BOOL is_polled;
struct hid_expect *pos;
struct hid_expect *end;
......@@ -379,7 +391,7 @@ struct input_queue
static void input_queue_init( struct input_queue *queue, BOOL is_polled )
{
KeInitializeSpinLock( &queue->lock );
KeInitializeSpinLock( &queue->base.lock );
queue->is_polled = is_polled;
queue->buffer = ExAllocatePool( PagedPool, EXPECT_QUEUE_BUFFER_SIZE );
RtlSecureZeroMemory( queue->buffer, EXPECT_QUEUE_BUFFER_SIZE );
......@@ -391,6 +403,7 @@ static void input_queue_init( struct input_queue *queue, BOOL is_polled )
static void input_queue_cleanup( struct input_queue *queue )
{
wait_queue_cleanup( &queue->base );
ExFreePool( queue->buffer );
}
......@@ -418,7 +431,7 @@ static NTSTATUS input_queue_read( struct input_queue *queue, IRP *irp )
NTSTATUS status;
KIRQL irql;
KeAcquireSpinLock( &queue->lock, &irql );
KeAcquireSpinLock( &queue->base.lock, &irql );
if (input_queue_read_locked( queue, irp ))
{
irp_queue_push( &queue->completed, irp );
......@@ -430,7 +443,7 @@ static NTSTATUS input_queue_read( struct input_queue *queue, IRP *irp )
irp_queue_push( &queue->pending, irp );
status = STATUS_PENDING;
}
KeReleaseSpinLock( &queue->lock, irql );
wait_queue_unlock( &queue->base, irql, queue->pos == queue->end );
return status;
}
......@@ -441,7 +454,7 @@ static void input_queue_reset( struct input_queue *queue, void *in_buf, ULONG in
KIRQL irql;
IRP *irp;
KeAcquireSpinLock( &queue->lock, &irql );
KeAcquireSpinLock( &queue->base.lock, &irql );
remaining = queue->end - queue->pos;
queue->pos = queue->buffer;
queue->end = queue->buffer;
......@@ -453,13 +466,28 @@ static void input_queue_reset( struct input_queue *queue, void *in_buf, ULONG in
input_queue_read_locked( queue, irp );
irp_queue_push( &queue->completed, irp );
}
KeReleaseSpinLock( &queue->lock, irql );
wait_queue_unlock( &queue->base, irql, queue->pos == queue->end );
if (!queue->is_polled) ok( !remaining, "unread input\n" );
irp_queue_complete( &queue->completed, FALSE );
}
/* wait for the input queue to empty */
static NTSTATUS input_queue_wait( struct input_queue *queue, IRP *irp )
{
NTSTATUS status;
KIRQL irql;
irp->IoStatus.Information = 0;
KeAcquireSpinLock( &queue->base.lock, &irql );
if (queue->pos == queue->end) status = STATUS_SUCCESS;
else status = wait_queue_add_pending_locked( &queue->base, irp );
KeReleaseSpinLock( &queue->base.lock, irql );
return status;
}
struct device
{
KSPIN_LOCK lock;
......@@ -1277,11 +1305,13 @@ static NTSTATUS pdo_handle_ioctl( struct phys_device *impl, IRP *irp, ULONG code
if (in_size > EXPECT_QUEUE_BUFFER_SIZE) return STATUS_BUFFER_OVERFLOW;
input_queue_reset( &impl->input_queue, in_buffer, in_size );
return STATUS_SUCCESS;
case IOCTL_WINETEST_HID_WAIT_INPUT:
return input_queue_wait( &impl->input_queue, irp );
case IOCTL_WINETEST_HID_SET_CONTEXT:
if (in_size > sizeof(impl->expect_queue.context)) return STATUS_BUFFER_OVERFLOW;
KeAcquireSpinLock( &impl->expect_queue.lock, &irql );
KeAcquireSpinLock( &impl->expect_queue.base.lock, &irql );
memcpy( impl->expect_queue.context, in_buffer, in_size );
KeReleaseSpinLock( &impl->expect_queue.lock, irql );
KeReleaseSpinLock( &impl->expect_queue.base.lock, irql );
return STATUS_SUCCESS;
case IOCTL_WINETEST_REMOVE_DEVICE:
KeAcquireSpinLock( &impl->base.lock, &irql );
......@@ -1356,6 +1386,7 @@ static NTSTATUS fdo_ioctl( DEVICE_OBJECT *device, IRP *irp )
case IOCTL_WINETEST_HID_WAIT_EXPECT:
case IOCTL_WINETEST_HID_SEND_INPUT:
case IOCTL_WINETEST_HID_SET_CONTEXT:
case IOCTL_WINETEST_HID_WAIT_INPUT:
if (in_size < sizeof(*desc))
status = STATUS_INVALID_PARAMETER;
else if (!(device = find_child_device( impl, desc )) || !(pdo = pdo_from_DEVICE_OBJECT( device )))
......
......@@ -188,6 +188,7 @@ static NTSTATUS WINAPI driver_ioctl( DEVICE_OBJECT *device, IRP *irp )
case IOCTL_WINETEST_HID_WAIT_EXPECT:
case IOCTL_WINETEST_HID_SEND_INPUT:
case IOCTL_WINETEST_HID_SET_CONTEXT:
case IOCTL_WINETEST_HID_WAIT_INPUT:
IoSkipCurrentIrpStackLocation( irp );
return IoCallDriver( ext->PhysicalDeviceObject, irp );
......
......@@ -49,6 +49,7 @@ DEFINE_GUID(control_class,0xdeadbeef,0x29ef,0x4538,0xa5,0xfd,0xb6,0x95,0x73,0xa3
#define IOCTL_WINETEST_HID_SET_CONTEXT CTL_CODE(FILE_DEVICE_KEYBOARD, 0x803, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
#define IOCTL_WINETEST_CREATE_DEVICE CTL_CODE(FILE_DEVICE_KEYBOARD, 0x804, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
#define IOCTL_WINETEST_REMOVE_DEVICE CTL_CODE(FILE_DEVICE_KEYBOARD, 0x805, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
#define IOCTL_WINETEST_HID_WAIT_INPUT CTL_CODE(FILE_DEVICE_KEYBOARD, 0x806, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
struct hid_expect
{
......@@ -173,6 +174,7 @@ static inline const char *debugstr_ioctl( ULONG code )
case IOCTL_WINETEST_HID_SET_EXPECT: return "IOCTL_WINETEST_HID_SET_EXPECT";
case IOCTL_WINETEST_HID_WAIT_EXPECT: return "IOCTL_WINETEST_HID_WAIT_EXPECT";
case IOCTL_WINETEST_HID_SEND_INPUT: return "IOCTL_WINETEST_HID_SEND_INPUT";
case IOCTL_WINETEST_HID_WAIT_INPUT: return "IOCTL_WINETEST_HID_WAIT_INPUT";
case IOCTL_WINETEST_HID_SET_CONTEXT: return "IOCTL_WINETEST_HID_SET_CONTEXT";
case IOCTL_WINETEST_CREATE_DEVICE: return "IOCTL_WINETEST_CREATE_DEVICE";
case IOCTL_WINETEST_REMOVE_DEVICE: return "IOCTL_WINETEST_REMOVE_DEVICE";
......
......@@ -1001,6 +1001,24 @@ void send_hid_input_( const char *file, int line, HANDLE device, struct hid_devi
ok_(file, line)( ret, "IOCTL_WINETEST_HID_SEND_INPUT failed, last error %lu\n", GetLastError() );
}
void wait_hid_input_( const char *file, int line, HANDLE device, struct hid_device_desc *desc,
DWORD timeout, BOOL todo )
{
char buffer[sizeof(*desc)];
SIZE_T size;
if (desc) memcpy( buffer, desc, sizeof(*desc) );
else memset( buffer, 0, sizeof(*desc) );
size = sizeof(*desc);
todo_wine_if(todo) {
BOOL ret = sync_ioctl_( file, line, device, IOCTL_WINETEST_HID_WAIT_INPUT, buffer, size, NULL, 0, timeout );
ok_(file, line)( ret, "IOCTL_WINETEST_HID_WAIT_INPUT failed, last error %lu\n", GetLastError() );
}
set_hid_expect_( file, line, device, desc, NULL, 0 );
}
static void test_hidp_get_input( HANDLE file, int report_id, ULONG report_len, PHIDP_PREPARSED_DATA preparsed )
{
struct hid_expect expect[] =
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment