hidclass.sys: Keep pending IRPs with the report queues.
Since d15358518b83384b137e81b71729c4f47fac0665 we only complete one pending IRP per HID report, but there may be more than one IRP queued, from different readers. This causes trouble and report interleaving when more than one reader accesses a device at a time. We need to complete only one for each report queue instead. Signed-off-by: Rémi Bernon <rbernon@codeweavers.com> Signed-off-by: Aric Stewart <aric@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
9421dd2b3c
commit
561e722a46
|
@ -33,45 +33,20 @@
|
|||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(hid);
|
||||
|
||||
IRP *pop_irp_from_queue(BASE_DEVICE_EXTENSION *ext)
|
||||
{
|
||||
LIST_ENTRY *entry;
|
||||
KIRQL old_irql;
|
||||
IRP *irp = NULL;
|
||||
|
||||
KeAcquireSpinLock(&ext->u.pdo.irp_queue_lock, &old_irql);
|
||||
|
||||
while (!irp && (entry = RemoveHeadList(&ext->u.pdo.irp_queue)) != &ext->u.pdo.irp_queue)
|
||||
{
|
||||
irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
|
||||
if (!IoSetCancelRoutine(irp, NULL))
|
||||
{
|
||||
/* cancel routine is already cleared, meaning that it was called. let it handle completion. */
|
||||
InitializeListHead(&irp->Tail.Overlay.ListEntry);
|
||||
irp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, old_irql);
|
||||
return irp;
|
||||
}
|
||||
|
||||
static void WINAPI read_cancel_routine(DEVICE_OBJECT *device, IRP *irp)
|
||||
{
|
||||
BASE_DEVICE_EXTENSION *ext;
|
||||
KIRQL old_irql;
|
||||
struct hid_report_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
|
||||
KIRQL irql;
|
||||
|
||||
TRACE("cancel %p IRP on device %p\n", irp, device);
|
||||
|
||||
ext = device->DeviceExtension;
|
||||
|
||||
IoReleaseCancelSpinLock(irp->CancelIrql);
|
||||
|
||||
KeAcquireSpinLock(&ext->u.pdo.irp_queue_lock, &old_irql);
|
||||
KeAcquireSpinLock( &queue->lock, &irql );
|
||||
|
||||
RemoveEntryList(&irp->Tail.Overlay.ListEntry);
|
||||
|
||||
KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, old_irql);
|
||||
KeReleaseSpinLock( &queue->lock, irql );
|
||||
|
||||
irp->IoStatus.Status = STATUS_CANCELLED;
|
||||
irp->IoStatus.Information = 0;
|
||||
|
@ -107,6 +82,7 @@ static struct hid_report_queue *hid_report_queue_create( void )
|
|||
struct hid_report_queue *queue;
|
||||
|
||||
if (!(queue = calloc( 1, sizeof(struct hid_report_queue) ))) return NULL;
|
||||
InitializeListHead( &queue->irp_queue );
|
||||
KeInitializeSpinLock( &queue->lock );
|
||||
list_init( &queue->entry );
|
||||
queue->length = 32;
|
||||
|
@ -116,8 +92,43 @@ static struct hid_report_queue *hid_report_queue_create( void )
|
|||
return queue;
|
||||
}
|
||||
|
||||
static IRP *hid_report_queue_pop_irp( struct hid_report_queue *queue )
|
||||
{
|
||||
LIST_ENTRY *entry;
|
||||
IRP *irp = NULL;
|
||||
KIRQL irql;
|
||||
|
||||
KeAcquireSpinLock( &queue->lock, &irql );
|
||||
|
||||
while (!irp && (entry = RemoveHeadList( &queue->irp_queue )) != &queue->irp_queue)
|
||||
{
|
||||
irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry );
|
||||
if (!IoSetCancelRoutine( irp, NULL ))
|
||||
{
|
||||
/* cancel routine is already cleared, meaning that it was called. let it handle completion. */
|
||||
InitializeListHead( &irp->Tail.Overlay.ListEntry );
|
||||
irp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
KeReleaseSpinLock( &queue->lock, irql );
|
||||
return irp;
|
||||
}
|
||||
|
||||
void hid_report_queue_remove_pending_irps( struct hid_report_queue *queue )
|
||||
{
|
||||
IRP *irp;
|
||||
|
||||
while ((irp = hid_report_queue_pop_irp( queue )))
|
||||
{
|
||||
irp->IoStatus.Status = STATUS_DELETE_PENDING;
|
||||
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
||||
}
|
||||
}
|
||||
|
||||
void hid_report_queue_destroy( struct hid_report_queue *queue )
|
||||
{
|
||||
hid_report_queue_remove_pending_irps( queue );
|
||||
while (queue->length--) hid_report_decref( queue->reports[queue->length] );
|
||||
list_remove( &queue->entry );
|
||||
free( queue );
|
||||
|
@ -144,7 +155,30 @@ static NTSTATUS hid_report_queue_resize( struct hid_report_queue *queue, ULONG l
|
|||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void hid_report_queue_push( struct hid_report_queue *queue, struct hid_report *report )
|
||||
static NTSTATUS hid_report_queue_push_irp( struct hid_report_queue *queue, IRP *irp )
|
||||
{
|
||||
KIRQL irql;
|
||||
|
||||
KeAcquireSpinLock( &queue->lock, &irql );
|
||||
|
||||
IoSetCancelRoutine( irp, read_cancel_routine );
|
||||
if (irp->Cancel && !IoSetCancelRoutine( irp, NULL ))
|
||||
{
|
||||
/* IRP was canceled before we set cancel routine */
|
||||
InitializeListHead( &irp->Tail.Overlay.ListEntry );
|
||||
KeReleaseSpinLock( &queue->lock, irql );
|
||||
return STATUS_CANCELLED;
|
||||
}
|
||||
|
||||
InsertTailList( &queue->irp_queue, &irp->Tail.Overlay.ListEntry );
|
||||
irp->IoStatus.Status = STATUS_PENDING;
|
||||
IoMarkIrpPending( irp );
|
||||
|
||||
KeReleaseSpinLock( &queue->lock, irql );
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
|
||||
static void hid_report_queue_push_report( struct hid_report_queue *queue, struct hid_report *report )
|
||||
{
|
||||
ULONG i = queue->write_idx, next = i + 1;
|
||||
struct hid_report *prev;
|
||||
|
@ -162,7 +196,7 @@ static void hid_report_queue_push( struct hid_report_queue *queue, struct hid_re
|
|||
hid_report_decref( prev );
|
||||
}
|
||||
|
||||
static struct hid_report *hid_report_queue_pop( struct hid_report_queue *queue )
|
||||
static struct hid_report *hid_report_queue_pop_report( struct hid_report_queue *queue )
|
||||
{
|
||||
ULONG i = queue->read_idx, next = i + 1;
|
||||
struct hid_report *report;
|
||||
|
@ -186,6 +220,7 @@ static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *pack
|
|||
const BOOL polled = ext->u.pdo.information.Polled;
|
||||
struct hid_report *last_report, *report;
|
||||
struct hid_report_queue *queue;
|
||||
LIST_ENTRY completed, *entry;
|
||||
RAWINPUT *rawinput;
|
||||
ULONG size;
|
||||
KIRQL irql;
|
||||
|
@ -223,25 +258,34 @@ static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *pack
|
|||
return;
|
||||
}
|
||||
|
||||
InitializeListHead( &completed );
|
||||
|
||||
KeAcquireSpinLock( &ext->u.pdo.report_queues_lock, &irql );
|
||||
LIST_FOR_EACH_ENTRY( queue, &ext->u.pdo.report_queues, struct hid_report_queue, entry )
|
||||
hid_report_queue_push( queue, last_report );
|
||||
{
|
||||
hid_report_queue_push_report( queue, last_report );
|
||||
|
||||
do
|
||||
{
|
||||
if (!(irp = hid_report_queue_pop_irp( queue ))) break;
|
||||
if (!(report = hid_report_queue_pop_report( queue ))) hid_report_incref( (report = last_report) );
|
||||
|
||||
memcpy( irp->AssociatedIrp.SystemBuffer, report->buffer, desc->InputLength );
|
||||
irp->IoStatus.Information = report->length;
|
||||
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
hid_report_decref( report );
|
||||
|
||||
InsertTailList( &completed, &irp->Tail.Overlay.ListEntry );
|
||||
}
|
||||
while (polled);
|
||||
}
|
||||
KeReleaseSpinLock( &ext->u.pdo.report_queues_lock, irql );
|
||||
|
||||
do
|
||||
while ((entry = RemoveHeadList( &completed )) != &completed)
|
||||
{
|
||||
if (!(irp = pop_irp_from_queue( ext ))) break;
|
||||
queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
|
||||
|
||||
if (!(report = hid_report_queue_pop( queue ))) hid_report_incref( (report = last_report) );
|
||||
memcpy( irp->AssociatedIrp.SystemBuffer, report->buffer, desc->InputLength );
|
||||
irp->IoStatus.Information = report->length;
|
||||
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
hid_report_decref( report );
|
||||
|
||||
irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry );
|
||||
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
||||
}
|
||||
while (polled);
|
||||
|
||||
hid_report_decref( last_report );
|
||||
}
|
||||
|
@ -584,7 +628,6 @@ NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp)
|
|||
HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
|
||||
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
|
||||
struct hid_report *report;
|
||||
NTSTATUS status;
|
||||
BOOL removed;
|
||||
KIRQL irql;
|
||||
|
||||
|
@ -607,36 +650,19 @@ NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp)
|
|||
}
|
||||
|
||||
irp->IoStatus.Information = 0;
|
||||
if ((report = hid_report_queue_pop( queue )))
|
||||
if ((report = hid_report_queue_pop_report( queue )))
|
||||
{
|
||||
memcpy( irp->AssociatedIrp.SystemBuffer, report->buffer, desc->InputLength );
|
||||
irp->IoStatus.Information = report->length;
|
||||
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
hid_report_decref( report );
|
||||
}
|
||||
else
|
||||
{
|
||||
KeAcquireSpinLock(&ext->u.pdo.irp_queue_lock, &irql);
|
||||
|
||||
IoSetCancelRoutine(irp, read_cancel_routine);
|
||||
if (irp->Cancel && !IoSetCancelRoutine(irp, NULL))
|
||||
{
|
||||
/* IRP was canceled before we set cancel routine */
|
||||
InitializeListHead(&irp->Tail.Overlay.ListEntry);
|
||||
KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, irql);
|
||||
return STATUS_CANCELLED;
|
||||
}
|
||||
|
||||
InsertTailList(&ext->u.pdo.irp_queue, &irp->Tail.Overlay.ListEntry);
|
||||
irp->IoStatus.Status = STATUS_PENDING;
|
||||
IoMarkIrpPending(irp);
|
||||
|
||||
KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, irql);
|
||||
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
status = irp->IoStatus.Status;
|
||||
if (status != STATUS_PENDING) IoCompleteRequest( irp, IO_NO_INCREMENT );
|
||||
return status;
|
||||
return hid_report_queue_push_irp( queue, irp );
|
||||
|
||||
}
|
||||
|
||||
NTSTATUS WINAPI pdo_write(DEVICE_OBJECT *device, IRP *irp)
|
||||
|
|
|
@ -67,9 +67,6 @@ typedef struct _BASE_DEVICE_EXTENSION
|
|||
|
||||
UNICODE_STRING link_name;
|
||||
|
||||
KSPIN_LOCK irp_queue_lock;
|
||||
LIST_ENTRY irp_queue;
|
||||
|
||||
KSPIN_LOCK lock;
|
||||
BOOL removed;
|
||||
|
||||
|
@ -104,6 +101,7 @@ struct hid_report_queue
|
|||
ULONG read_idx;
|
||||
ULONG write_idx;
|
||||
struct hid_report *reports[512];
|
||||
LIST_ENTRY irp_queue;
|
||||
};
|
||||
|
||||
typedef struct _minidriver
|
||||
|
@ -123,8 +121,8 @@ void call_minidriver( ULONG code, DEVICE_OBJECT *device, void *in_buff, ULONG in
|
|||
|
||||
/* Internal device functions */
|
||||
void HID_StartDeviceThread(DEVICE_OBJECT *device) DECLSPEC_HIDDEN;
|
||||
void hid_report_queue_remove_pending_irps( struct hid_report_queue *queue );
|
||||
void hid_report_queue_destroy( struct hid_report_queue *queue );
|
||||
IRP *pop_irp_from_queue(BASE_DEVICE_EXTENSION *ext) DECLSPEC_HIDDEN;
|
||||
|
||||
NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN;
|
||||
NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp) DECLSPEC_HIDDEN;
|
||||
|
|
|
@ -222,8 +222,6 @@ static void create_child(minidriver *minidriver, DEVICE_OBJECT *fdo)
|
|||
pdo_ext->u.pdo.parent_fdo = fdo;
|
||||
list_init( &pdo_ext->u.pdo.report_queues );
|
||||
KeInitializeSpinLock( &pdo_ext->u.pdo.report_queues_lock );
|
||||
InitializeListHead(&pdo_ext->u.pdo.irp_queue);
|
||||
KeInitializeSpinLock(&pdo_ext->u.pdo.irp_queue_lock);
|
||||
wcscpy(pdo_ext->device_id, fdo_ext->device_id);
|
||||
wcscpy(pdo_ext->instance_id, fdo_ext->instance_id);
|
||||
pdo_ext->class_guid = fdo_ext->class_guid;
|
||||
|
@ -370,17 +368,6 @@ static NTSTATUS fdo_pnp(DEVICE_OBJECT *device, IRP *irp)
|
|||
}
|
||||
}
|
||||
|
||||
static void remove_pending_irps(BASE_DEVICE_EXTENSION *ext)
|
||||
{
|
||||
IRP *irp;
|
||||
|
||||
while ((irp = pop_irp_from_queue(ext)))
|
||||
{
|
||||
irp->IoStatus.Status = STATUS_DELETE_PENDING;
|
||||
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp)
|
||||
{
|
||||
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
|
||||
|
@ -482,8 +469,6 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp)
|
|||
break;
|
||||
|
||||
case IRP_MN_REMOVE_DEVICE:
|
||||
remove_pending_irps(ext);
|
||||
|
||||
send_wm_input_device_change(ext, GIDC_REMOVAL);
|
||||
|
||||
IoSetDeviceInterfaceState(&ext->u.pdo.link_name, FALSE);
|
||||
|
@ -518,7 +503,10 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp)
|
|||
ext->u.pdo.removed = TRUE;
|
||||
KeReleaseSpinLock(&ext->u.pdo.lock, irql);
|
||||
|
||||
remove_pending_irps(ext);
|
||||
KeAcquireSpinLock( &ext->u.pdo.report_queues_lock, &irql );
|
||||
LIST_FOR_EACH_ENTRY_SAFE( queue, next, &ext->u.pdo.report_queues, struct hid_report_queue, entry )
|
||||
hid_report_queue_remove_pending_irps( queue );
|
||||
KeReleaseSpinLock( &ext->u.pdo.report_queues_lock, irql );
|
||||
|
||||
SetEvent(ext->u.pdo.halt_event);
|
||||
status = STATUS_SUCCESS;
|
||||
|
|
Loading…
Reference in New Issue