Skip to content
Snippets Groups Projects
Commit 13a44865 authored by Rémi Bernon's avatar Rémi Bernon :speech_balloon: Committed by Alexandre Julliard
Browse files

hidclass: Start the HID device thread with the FDO.

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: c65d5b09
parent 4d217db0
No related branches found
No related tags found
No related merge requests found
...@@ -312,11 +312,11 @@ static HIDP_REPORT_IDS *find_report_with_type_and_id( HIDP_DEVICE_DESC *desc, UC ...@@ -312,11 +312,11 @@ static HIDP_REPORT_IDS *find_report_with_type_and_id( HIDP_DEVICE_DESC *desc, UC
return NULL; return NULL;
} }
static DWORD CALLBACK hid_device_thread(void *args) DWORD CALLBACK hid_device_thread(void *args)
{ {
DEVICE_OBJECT *device = (DEVICE_OBJECT*)args; DEVICE_OBJECT *device = (DEVICE_OBJECT*)args;
BASE_DEVICE_EXTENSION *ext = device->DeviceExtension, *fdo_ext; BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
ULONG i, collection, input_length = 0, report_id = 0; ULONG i, input_length = 0, report_id = 0;
HIDP_REPORT_IDS *report; HIDP_REPORT_IDS *report;
HID_XFER_PACKET *packet; HID_XFER_PACKET *packet;
HIDP_DEVICE_DESC *desc; HIDP_DEVICE_DESC *desc;
...@@ -324,19 +324,17 @@ static DWORD CALLBACK hid_device_thread(void *args) ...@@ -324,19 +324,17 @@ static DWORD CALLBACK hid_device_thread(void *args)
BYTE *buffer; BYTE *buffer;
DWORD res; DWORD res;
fdo_ext = ext->u.pdo.parent_fdo->DeviceExtension; for (i = 0; i < ext->u.fdo.device_desc.CollectionDescLength; i++)
for (i = 0; i < fdo_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); input_length = max(input_length, desc->InputLength);
} }
packet = malloc( sizeof(*packet) + input_length ); packet = malloc( sizeof(*packet) + input_length );
buffer = (BYTE *)(packet + 1); buffer = (BYTE *)(packet + 1);
desc = &fdo_ext->u.fdo.device_desc; desc = &ext->u.fdo.device_desc;
collection = ext->u.pdo.collection_desc->CollectionNumber; report = find_report_with_type_and_id( desc, 0, HidP_Input, 0, TRUE );
report = find_report_with_type_and_id( desc, collection, HidP_Input, 0, TRUE );
if (!report) WARN("no input report found.\n"); if (!report) WARN("no input report found.\n");
else report_id = report->ReportID; else report_id = report->ReportID;
...@@ -352,39 +350,35 @@ static DWORD CALLBACK hid_device_thread(void *args) ...@@ -352,39 +350,35 @@ static DWORD CALLBACK hid_device_thread(void *args)
packet->reportBufferLen--; 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 ); packet->reportBuffer, packet->reportBufferLen, &io );
if (io.Status == STATUS_SUCCESS) if (io.Status == STATUS_SUCCESS)
{ {
if (!report_id) io.Information++; 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] ); 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 ); 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 else
{ {
DEVICE_OBJECT *pdo = ext->u.fdo.child_pdos[report->CollectionNumber - 1];
packet->reportId = buffer[0]; packet->reportId = buffer[0];
packet->reportBuffer = buffer; packet->reportBuffer = buffer;
packet->reportBufferLen = io.Information; 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); } while (res == WAIT_TIMEOUT);
TRACE( "device thread exiting, res %#lx\n", res ); TRACE( "device thread exiting, res %#lx\n", res );
return 1; 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 struct device_strings
{ {
const WCHAR *id; const WCHAR *id;
...@@ -567,7 +561,7 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) ...@@ -567,7 +561,7 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp)
status = STATUS_BUFFER_OVERFLOW; status = STATUS_BUFFER_OVERFLOW;
else else
{ {
*(ULONG *)irp->AssociatedIrp.SystemBuffer = ext->u.pdo.poll_interval; *(ULONG *)irp->AssociatedIrp.SystemBuffer = ext->u.fdo.poll_interval;
irp->IoStatus.Information = sizeof(ULONG); irp->IoStatus.Information = sizeof(ULONG);
status = STATUS_SUCCESS; status = STATUS_SUCCESS;
} }
...@@ -580,7 +574,7 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp) ...@@ -580,7 +574,7 @@ NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp)
else else
{ {
poll_interval = *(ULONG *)irp->AssociatedIrp.SystemBuffer; 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; status = STATUS_SUCCESS;
} }
break; break;
......
...@@ -52,6 +52,10 @@ typedef struct _BASE_DEVICE_EXTENSION ...@@ -52,6 +52,10 @@ typedef struct _BASE_DEVICE_EXTENSION
HIDP_DEVICE_DESC device_desc; HIDP_DEVICE_DESC device_desc;
WCHAR serial[256]; WCHAR serial[256];
ULONG poll_interval;
HANDLE halt_event;
HANDLE thread;
DEVICE_OBJECT **child_pdos; DEVICE_OBJECT **child_pdos;
UINT child_count; UINT child_count;
} fdo; } fdo;
...@@ -63,9 +67,6 @@ typedef struct _BASE_DEVICE_EXTENSION ...@@ -63,9 +67,6 @@ typedef struct _BASE_DEVICE_EXTENSION
HIDP_COLLECTION_DESC *collection_desc; HIDP_COLLECTION_DESC *collection_desc;
HID_COLLECTION_INFORMATION information; HID_COLLECTION_INFORMATION information;
ULONG poll_interval;
HANDLE halt_event;
HANDLE thread;
UINT32 rawinput_handle; UINT32 rawinput_handle;
UNICODE_STRING link_name; UNICODE_STRING link_name;
...@@ -124,7 +125,7 @@ void call_minidriver( ULONG code, DEVICE_OBJECT *device, void *in_buff, ULONG in ...@@ -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 ); void *out_buff, ULONG out_size, IO_STATUS_BLOCK *io );
/* Internal device functions */ /* 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_remove_pending_irps( struct hid_queue *queue );
void hid_queue_destroy( struct hid_queue *queue ); void hid_queue_destroy( struct hid_queue *queue );
......
...@@ -250,6 +250,8 @@ static NTSTATUS initialize_device( minidriver *minidriver, DEVICE_OBJECT *device ...@@ -250,6 +250,8 @@ static NTSTATUS initialize_device( minidriver *minidriver, DEVICE_OBJECT *device
return STATUS_NO_MEMORY; 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; return STATUS_SUCCESS;
} }
...@@ -318,7 +320,6 @@ static NTSTATUS create_child_pdos( minidriver *minidriver, DEVICE_OBJECT *device ...@@ -318,7 +320,6 @@ static NTSTATUS create_child_pdos( minidriver *minidriver, DEVICE_OBJECT *device
pdo_ext->u.pdo.rawinput_handle = WINE_KEYBOARD_HANDLE; pdo_ext->u.pdo.rawinput_handle = WINE_KEYBOARD_HANDLE;
else else
pdo_ext->u.pdo.rawinput_handle = alloc_rawinput_handle(); 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 ); 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) ...@@ -370,9 +371,17 @@ static NTSTATUS fdo_pnp(DEVICE_OBJECT *device, IRP *irp)
status = minidriver->PNPDispatch( device, irp ); status = minidriver->PNPDispatch( device, irp );
if (!status) status = initialize_device( minidriver, device ); if (!status) status = initialize_device( minidriver, device );
if (!status) status = create_child_pdos( 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; return status;
case IRP_MN_REMOVE_DEVICE: 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 ); status = minidriver->PNPDispatch( device, irp );
HidP_FreeCollectionDescription( &ext->u.fdo.device_desc ); HidP_FreeCollectionDescription( &ext->u.fdo.device_desc );
free( ext->u.fdo.child_pdos ); free( ext->u.fdo.child_pdos );
...@@ -380,6 +389,10 @@ static NTSTATUS fdo_pnp(DEVICE_OBJECT *device, IRP *irp) ...@@ -380,6 +389,10 @@ static NTSTATUS fdo_pnp(DEVICE_OBJECT *device, IRP *irp)
IoDeleteDevice( device ); IoDeleteDevice( device );
return status; return status;
case IRP_MN_SURPRISE_REMOVAL:
SetEvent(ext->u.fdo.halt_event);
return STATUS_SUCCESS;
default: default:
return minidriver->PNPDispatch(device, irp); return minidriver->PNPDispatch(device, irp);
} }
...@@ -522,7 +535,6 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp) ...@@ -522,7 +535,6 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp)
} }
case IRP_MN_START_DEVICE: case IRP_MN_START_DEVICE:
HID_StartDeviceThread(device);
send_wm_input_device_change(ext, GIDC_ARRIVAL); send_wm_input_device_change(ext, GIDC_ARRIVAL);
if ((status = IoRegisterDeviceInterface(device, ext->class_guid, NULL, &ext->u.pdo.link_name))) 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) ...@@ -562,13 +574,6 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp)
if (ext->u.pdo.is_keyboard) if (ext->u.pdo.is_keyboard)
IoSetDeviceInterfaceState(&ext->u.pdo.keyboard_link_name, FALSE); 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 ); KeAcquireSpinLock( &ext->u.pdo.lock, &irql );
LIST_FOR_EACH_ENTRY_SAFE( queue, next, &ext->u.pdo.queues, struct hid_queue, entry ) LIST_FOR_EACH_ENTRY_SAFE( queue, next, &ext->u.pdo.queues, struct hid_queue, entry )
hid_queue_destroy( queue ); hid_queue_destroy( queue );
...@@ -588,7 +593,6 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp) ...@@ -588,7 +593,6 @@ static NTSTATUS pdo_pnp(DEVICE_OBJECT *device, IRP *irp)
hid_queue_remove_pending_irps( queue ); hid_queue_remove_pending_irps( queue );
KeReleaseSpinLock( &ext->u.pdo.lock, irql ); KeReleaseSpinLock( &ext->u.pdo.lock, irql );
SetEvent(ext->u.pdo.halt_event);
status = STATUS_SUCCESS; status = STATUS_SUCCESS;
break; break;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment