dinput/tests: Add a helper to wait on HID input reads.
This commit is contained in:
parent
a4d2e62a2f
commit
bd2b89806a
6 changed files with 119 additions and 61 deletions
|
@ -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[] =
|
||||
|
|
Loading…
Add table
Reference in a new issue