mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[idd] input: purge report reads asynchronously
WDF forbids WdfIoQueuePurgeSynchronously from an I/O callback. Keep HID deactivation pending while the report queue is purged asynchronously, then complete it from the queue-state callback. Reject activation during the transition. Complete any remaining deactivation request during queue cleanup.
This commit is contained in:
@@ -80,6 +80,7 @@ struct HIDDeviceContext
|
|||||||
HIDStatistics statistics;
|
HIDStatistics statistics;
|
||||||
HIDQueuedReport reports[REPORT_QUEUE_LENGTH];
|
HIDQueuedReport reports[REPORT_QUEUE_LENGTH];
|
||||||
CInputPipeClient * inputPipe;
|
CInputPipeClient * inputPipe;
|
||||||
|
WDFREQUEST deactivateRequest;
|
||||||
};
|
};
|
||||||
|
|
||||||
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(HIDDeviceContext, HIDGetDeviceContext);
|
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(HIDDeviceContext, HIDGetDeviceContext);
|
||||||
@@ -88,6 +89,7 @@ static CSRWLock s_deviceLock;
|
|||||||
static HIDDeviceContext * s_device = nullptr;
|
static HIDDeviceContext * s_device = nullptr;
|
||||||
|
|
||||||
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL HIDEvtIoDeviceControl;
|
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL HIDEvtIoDeviceControl;
|
||||||
|
EVT_WDF_IO_QUEUE_STATE HIDEvtReportQueuePurged;
|
||||||
EVT_WDF_OBJECT_CONTEXT_CLEANUP HIDEvtReportQueueCleanup;
|
EVT_WDF_OBJECT_CONTEXT_CLEANUP HIDEvtReportQueueCleanup;
|
||||||
EVT_WDF_OBJECT_CONTEXT_CLEANUP HIDEvtDeviceCleanup;
|
EVT_WDF_OBJECT_CONTEXT_CLEANUP HIDEvtDeviceCleanup;
|
||||||
EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT HIDEvtSelfManagedIoInit;
|
EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT HIDEvtSelfManagedIoInit;
|
||||||
@@ -338,7 +340,7 @@ static NTSTATUS ActivateDevice(_Inout_ HIDDeviceContext * context)
|
|||||||
{
|
{
|
||||||
CSRWExclusiveLock lifecycleLock(context->lifecycleLock);
|
CSRWExclusiveLock lifecycleLock(context->lifecycleLock);
|
||||||
CSRWExclusiveLock lock(context->reportLock);
|
CSRWExclusiveLock lock(context->reportLock);
|
||||||
if (context->stopping)
|
if (context->stopping || context->deactivateRequest)
|
||||||
return STATUS_DEVICE_NOT_READY;
|
return STATUS_DEVICE_NOT_READY;
|
||||||
|
|
||||||
WdfIoQueueStart(context->reportQueue);
|
WdfIoQueueStart(context->reportQueue);
|
||||||
@@ -346,21 +348,36 @@ static NTSTATUS ActivateDevice(_Inout_ HIDDeviceContext * context)
|
|||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NTSTATUS DeactivateDevice(_Inout_ HIDDeviceContext * context)
|
static NTSTATUS DeactivateDevice(
|
||||||
|
_Inout_ HIDDeviceContext * context,
|
||||||
|
_In_ WDFREQUEST request,
|
||||||
|
_Out_ bool * complete)
|
||||||
{
|
{
|
||||||
CSRWExclusiveLock lifecycleLock(context->lifecycleLock);
|
|
||||||
{
|
{
|
||||||
CSRWExclusiveLock lock(context->reportLock);
|
CSRWExclusiveLock lifecycleLock(context->lifecycleLock);
|
||||||
if (context->stopping)
|
{
|
||||||
return STATUS_DEVICE_NOT_READY;
|
CSRWExclusiveLock lock(context->reportLock);
|
||||||
|
if (context->stopping)
|
||||||
|
return STATUS_DEVICE_NOT_READY;
|
||||||
|
if (context->deactivateRequest)
|
||||||
|
return STATUS_DEVICE_BUSY;
|
||||||
|
|
||||||
context->active = false;
|
context->active = false;
|
||||||
context->reportHead = 0;
|
context->reportHead = 0;
|
||||||
context->reportCount = 0;
|
context->reportCount = 0;
|
||||||
context->consumerUsage = UINT16_MAX;
|
context->consumerUsage = UINT16_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->deactivateRequest = request;
|
||||||
}
|
}
|
||||||
WdfIoQueuePurgeSynchronously(context->reportQueue);
|
|
||||||
return STATUS_SUCCESS;
|
// WDF forbids synchronously purging any queue from EvtIoDeviceControl.
|
||||||
|
// Keep the HID deactivation request pending until the asynchronous purge
|
||||||
|
// has canceled every outstanding read report.
|
||||||
|
*complete = false;
|
||||||
|
WdfIoQueuePurge(
|
||||||
|
context->reportQueue, HIDEvtReportQueuePurged, context);
|
||||||
|
return STATUS_PENDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NTSTATUS CreateQueues(_In_ WDFDEVICE device)
|
static NTSTATUS CreateQueues(_In_ WDFDEVICE device)
|
||||||
@@ -737,7 +754,7 @@ VOID HIDEvtIoDeviceControl(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case IOCTL_HID_DEACTIVATE_DEVICE:
|
case IOCTL_HID_DEACTIVATE_DEVICE:
|
||||||
status = DeactivateDevice(context);
|
status = DeactivateDevice(context, request, &complete);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -749,21 +766,51 @@ VOID HIDEvtIoDeviceControl(
|
|||||||
WdfRequestComplete(request, status);
|
WdfRequestComplete(request, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOID HIDEvtReportQueuePurged(
|
||||||
|
_In_ WDFQUEUE queue,
|
||||||
|
_In_ WDFCONTEXT callbackContext)
|
||||||
|
{
|
||||||
|
UNREFERENCED_PARAMETER(queue);
|
||||||
|
|
||||||
|
HIDDeviceContext * context =
|
||||||
|
static_cast<HIDDeviceContext *>(callbackContext);
|
||||||
|
WDFREQUEST request = nullptr;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lifecycleLock(context->lifecycleLock);
|
||||||
|
request = context->deactivateRequest;
|
||||||
|
context->deactivateRequest = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request)
|
||||||
|
WdfRequestComplete(request, STATUS_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
VOID HIDEvtReportQueueCleanup(_In_ WDFOBJECT object)
|
VOID HIDEvtReportQueueCleanup(_In_ WDFOBJECT object)
|
||||||
{
|
{
|
||||||
HIDDeviceContext * context =
|
HIDDeviceContext * context =
|
||||||
HIDGetDeviceContext(WdfIoQueueGetDevice((WDFQUEUE)object));
|
HIDGetDeviceContext(WdfIoQueueGetDevice((WDFQUEUE)object));
|
||||||
|
|
||||||
CSRWExclusiveLock deviceLock(s_deviceLock);
|
|
||||||
if (s_device == context)
|
|
||||||
s_device = nullptr;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
CSRWExclusiveLock reportLock(context->reportLock);
|
CSRWExclusiveLock deviceLock(s_deviceLock);
|
||||||
context->stopping = true;
|
if (s_device == context)
|
||||||
context->reportHead = 0;
|
s_device = nullptr;
|
||||||
context->reportCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WDFREQUEST deactivateRequest = nullptr;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lifecycleLock(context->lifecycleLock);
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock reportLock(context->reportLock);
|
||||||
|
context->stopping = true;
|
||||||
|
context->reportHead = 0;
|
||||||
|
context->reportCount = 0;
|
||||||
|
}
|
||||||
|
deactivateRequest = context->deactivateRequest;
|
||||||
|
context->deactivateRequest = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deactivateRequest)
|
||||||
|
WdfRequestComplete(deactivateRequest, STATUS_DEVICE_REMOVED);
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID HIDEvtDeviceCleanup(_In_ WDFOBJECT object)
|
VOID HIDEvtDeviceCleanup(_In_ WDFOBJECT object)
|
||||||
|
|||||||
Reference in New Issue
Block a user