From: Rémi Bernon rbernon@codeweavers.com
And dispatch reports to the corresponding child PDO report queues.
Otherwise, with multi-TLC devices, concurrent PDO threads would steal reports from each other, which causes random timeouts in the tests.
Fixes: c65d5b094825afdafba462a4050a490426f3cd51 --- dlls/hidclass.sys/device.c | 40 ++++++++++++++++---------------------- dlls/hidclass.sys/hid.h | 9 +++++---- dlls/hidclass.sys/pnp.c | 24 +++++++++++++---------- 3 files changed, 36 insertions(+), 37 deletions(-)
diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c index b47df9ecf51..440bf4de24c 100644 --- a/dlls/hidclass.sys/device.c +++ b/dlls/hidclass.sys/device.c @@ -312,11 +312,11 @@ static HIDP_REPORT_IDS *find_report_with_type_and_id( HIDP_DEVICE_DESC *desc, UC return NULL; }
-static DWORD CALLBACK hid_device_thread(void *args) +DWORD CALLBACK hid_device_thread(void *args) { DEVICE_OBJECT *device = (DEVICE_OBJECT*)args; - BASE_DEVICE_EXTENSION *ext = device->DeviceExtension, *fdo_ext; - ULONG i, collection, input_length = 0, report_id = 0; + BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; + ULONG i, input_length = 0, report_id = 0; HIDP_REPORT_IDS *report; HID_XFER_PACKET *packet; HIDP_DEVICE_DESC *desc; @@ -324,19 +324,17 @@ static DWORD CALLBACK hid_device_thread(void *args) BYTE *buffer; DWORD res;
- fdo_ext = ext->u.pdo.parent_fdo->DeviceExtension; - for (i = 0; i < fdo_ext->u.fdo.device_desc.CollectionDescLength; i++) + for (i = 0; i < ext->u.fdo.device_desc.CollectionDescLength; i++) { - HIDP_COLLECTION_DESC *desc = fdo_ext->u.fdo.device_desc.CollectionDesc + i; + HIDP_COLLECTION_DESC *desc = ext->u.fdo.device_desc.CollectionDesc + i; input_length = max(input_length, desc->InputLength); }
packet = malloc( sizeof(*packet) + input_length ); buffer = (BYTE *)(packet + 1);
- desc = &fdo_ext->u.fdo.device_desc; - collection = ext->u.pdo.collection_desc->CollectionNumber; - report = find_report_with_type_and_id( desc, collection, HidP_Input, 0, TRUE ); + desc = &ext->u.fdo.device_desc; + report = find_report_with_type_and_id( desc, 0, HidP_Input, 0, TRUE ); if (!report) WARN("no input report found.\n"); else report_id = report->ReportID;
@@ -352,39 +350,35 @@ static DWORD CALLBACK hid_device_thread(void *args) packet->reportBufferLen--; }
- call_minidriver( IOCTL_HID_READ_REPORT, ext->u.pdo.parent_fdo, NULL, 0, + call_minidriver( IOCTL_HID_READ_REPORT, device, NULL, 0, packet->reportBuffer, packet->reportBufferLen, &io );
if (io.Status == STATUS_SUCCESS) { if (!report_id) io.Information++; - if (!(report = find_report_with_type_and_id( desc, collection, HidP_Input, buffer[0], FALSE ))) + if (!(report = find_report_with_type_and_id( desc, 0, HidP_Input, buffer[0], FALSE ))) ERR( "dropping unknown input id %u\n", buffer[0] ); - else if (!ext->u.pdo.poll_interval && io.Information < report->InputLength) + else if (!ext->u.fdo.poll_interval && io.Information < report->InputLength) ERR( "dropping short report, len %Iu expected %u\n", io.Information, report->InputLength ); + else if (!report->CollectionNumber || report->CollectionNumber > ext->u.fdo.child_count) + ERR( "dropping report for unknown child %u\n", report->CollectionNumber ); else { + DEVICE_OBJECT *pdo = ext->u.fdo.child_pdos[report->CollectionNumber - 1]; packet->reportId = buffer[0]; packet->reportBuffer = buffer; packet->reportBufferLen = io.Information; - hid_device_queue_input( device, packet, !!ext->u.pdo.poll_interval ); + hid_device_queue_input( pdo, packet, !!ext->u.fdo.poll_interval ); } }
- res = WaitForSingleObject( ext->u.pdo.halt_event, ext->u.pdo.poll_interval ); + res = WaitForSingleObject( ext->u.fdo.halt_event, ext->u.fdo.poll_interval ); } while (res == WAIT_TIMEOUT);
TRACE( "device thread exiting, res %#lx\n", res ); return 1; }
-void HID_StartDeviceThread(DEVICE_OBJECT *device) -{ - BASE_DEVICE_EXTENSION *ext = device->DeviceExtension; - ext->u.pdo.halt_event = CreateEventA(NULL, TRUE, FALSE, NULL); - ext->u.pdo.thread = CreateThread(NULL, 0, hid_device_thread, device, 0, NULL); -} - struct device_strings { const WCHAR *id; @@ -567,7 +561,7 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) status = STATUS_BUFFER_OVERFLOW; else { - *(ULONG *)irp->AssociatedIrp.SystemBuffer = ext->u.pdo.poll_interval; + *(ULONG *)irp->AssociatedIrp.SystemBuffer = ext->u.fdo.poll_interval; irp->IoStatus.Information = sizeof(ULONG); status = STATUS_SUCCESS; } @@ -580,7 +574,7 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) else { poll_interval = *(ULONG *)irp->AssociatedIrp.SystemBuffer; - if (poll_interval) ext->u.pdo.poll_interval = min( poll_interval, MAX_POLL_INTERVAL_MSEC ); + if (poll_interval) ext->u.fdo.poll_interval = min( poll_interval, MAX_POLL_INTERVAL_MSEC ); status = STATUS_SUCCESS; } break; diff --git a/dlls/hidclass.sys/hid.h b/dlls/hidclass.sys/hid.h index 89f9bb54c4b..d58143b34b1 100644 --- a/dlls/hidclass.sys/hid.h +++ b/dlls/hidclass.sys/hid.h @@ -52,6 +52,10 @@ typedef struct _BASE_DEVICE_EXTENSION HIDP_DEVICE_DESC device_desc; WCHAR serial[256];
+ ULONG poll_interval; + HANDLE halt_event; + HANDLE thread; + DEVICE_OBJECT **child_pdos; UINT child_count; } fdo; @@ -63,9 +67,6 @@ typedef struct _BASE_DEVICE_EXTENSION HIDP_COLLECTION_DESC *collection_desc; HID_COLLECTION_INFORMATION information;
- ULONG poll_interval; - HANDLE halt_event; - HANDLE thread; UINT32 rawinput_handle; UNICODE_STRING link_name;
@@ -124,7 +125,7 @@ void call_minidriver( ULONG code, DEVICE_OBJECT *device, void *in_buff, ULONG in void *out_buff, ULONG out_size, IO_STATUS_BLOCK *io );
/* Internal device functions */ -void HID_StartDeviceThread( DEVICE_OBJECT *device ); +DWORD CALLBACK hid_device_thread(void *args); void hid_queue_remove_pending_irps( struct hid_queue *queue ); void hid_queue_destroy( struct hid_queue *queue );
diff --git a/dlls/hidclass.sys/pnp.c b/dlls/hidclass.sys/pnp.c index f866f011dc8..e6ebbb05b32 100644 --- a/dlls/hidclass.sys/pnp.c +++ b/dlls/hidclass.sys/pnp.c @@ -250,6 +250,8 @@ static NTSTATUS initialize_device( minidriver *minidriver, DEVICE_OBJECT *device return STATUS_NO_MEMORY; }
+ ext->u.fdo.poll_interval = minidriver->minidriver.DevicesArePolled ? DEFAULT_POLL_INTERVAL : 0; + ext->u.fdo.halt_event = CreateEventA(NULL, TRUE, FALSE, NULL); return STATUS_SUCCESS; }
@@ -318,7 +320,6 @@ static NTSTATUS create_child_pdos( minidriver *minidriver, DEVICE_OBJECT *device pdo_ext->u.pdo.rawinput_handle = WINE_KEYBOARD_HANDLE; else pdo_ext->u.pdo.rawinput_handle = alloc_rawinput_handle(); - pdo_ext->u.pdo.poll_interval = minidriver->minidriver.DevicesArePolled ? DEFAULT_POLL_INTERVAL : 0;
TRACE( "created device %p, rawinput handle %#x\n", pdo_ext, pdo_ext->u.pdo.rawinput_handle ); } @@ -370,9 +371,17 @@ static NTSTATUS fdo_pnp(DEVICE_OBJECT *device, IRP *irp) status = minidriver->PNPDispatch( device, irp ); if (!status) status = initialize_device( minidriver, device ); if (!status) status = create_child_pdos( minidriver, device ); + if (!status) ext->u.fdo.thread = CreateThread(NULL, 0, hid_device_thread, device, 0, NULL); return status;
case IRP_MN_REMOVE_DEVICE: + if (ext->u.fdo.thread) + { + SetEvent(ext->u.fdo.halt_event); + WaitForSingleObject(ext->u.fdo.thread, INFINITE); + } + CloseHandle(ext->u.fdo.halt_event); + status = minidriver->PNPDispatch( device, irp ); HidP_FreeCollectionDescription( &ext->u.fdo.device_desc ); free( ext->u.fdo.child_pdos ); @@ -380,6 +389,10 @@ static NTSTATUS fdo_pnp(DEVICE_OBJECT *device, IRP *irp) IoDeleteDevice( device ); return status;
+ case IRP_MN_SURPRISE_REMOVAL: + SetEvent(ext->u.fdo.halt_event); + return STATUS_SUCCESS; + default: return minidriver->PNPDispatch(device, irp); } @@ -522,7 +535,6 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp) }
case IRP_MN_START_DEVICE: - HID_StartDeviceThread(device); send_wm_input_device_change(ext, GIDC_ARRIVAL);
if ((status = IoRegisterDeviceInterface(device, ext->class_guid, NULL, &ext->u.pdo.link_name))) @@ -562,13 +574,6 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp) if (ext->u.pdo.is_keyboard) IoSetDeviceInterfaceState(&ext->u.pdo.keyboard_link_name, FALSE);
- if (ext->u.pdo.thread) - { - SetEvent(ext->u.pdo.halt_event); - WaitForSingleObject(ext->u.pdo.thread, INFINITE); - } - CloseHandle(ext->u.pdo.halt_event); - KeAcquireSpinLock( &ext->u.pdo.lock, &irql ); LIST_FOR_EACH_ENTRY_SAFE( queue, next, &ext->u.pdo.queues, struct hid_queue, entry ) hid_queue_destroy( queue ); @@ -588,7 +593,6 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp) hid_queue_remove_pending_irps( queue ); KeReleaseSpinLock( &ext->u.pdo.lock, irql );
- SetEvent(ext->u.pdo.halt_event); status = STATUS_SUCCESS; break;