You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
321 lines
8.4 KiB
321 lines
8.4 KiB
/*****************************************************************************
|
|
* callback.cpp - Generic unload safe callbacks (where possible)
|
|
*****************************************************************************
|
|
* Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
#include "private.h"
|
|
|
|
VOID
|
|
EnqueuedIoWorkItemCallback(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
EnqueuedDpcCallback(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
);
|
|
|
|
#pragma code_seg()
|
|
|
|
NTSTATUS
|
|
CallbackEnqueue(
|
|
IN OUT PVOID *pCallbackHandle OPTIONAL,
|
|
IN PFNQUEUED_CALLBACK CallbackRoutine,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context,
|
|
IN KIRQL Irql,
|
|
IN ULONG Flags
|
|
)
|
|
{
|
|
PQUEUED_CALLBACK_ITEM pQueuedCallbackItem;
|
|
|
|
//
|
|
// Check the flags we understand. If it's not understood, and this is the
|
|
// class of flags support is required for, bail immediately.
|
|
//
|
|
if ((Flags & (~EQCM_SUPPORTED_FLAGS)) & EQCM_SUPPORT_OR_FAIL_FLAGS) {
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if ((Irql != PASSIVE_LEVEL) && (Irql != DISPATCH_LEVEL)) {
|
|
|
|
ASSERT(0);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (Flags & EQCF_REUSE_HANDLE) {
|
|
|
|
ASSERT(pCallbackHandle);
|
|
pQueuedCallbackItem = (PQUEUED_CALLBACK_ITEM) *pCallbackHandle;
|
|
|
|
//
|
|
// Shouldn't already be enqueued.
|
|
//
|
|
ASSERT(pQueuedCallbackItem->Enqueued == 0);
|
|
|
|
} else {
|
|
|
|
pQueuedCallbackItem = (PQUEUED_CALLBACK_ITEM) ExAllocatePoolWithTag(
|
|
((KeGetCurrentIrql() == PASSIVE_LEVEL) && (Irql == PASSIVE_LEVEL)) ?
|
|
PagedPool : NonPagedPool,
|
|
sizeof(QUEUED_CALLBACK_ITEM),
|
|
'bCcP'
|
|
); // 'PcCb'
|
|
|
|
if (pQueuedCallbackItem) {
|
|
|
|
pQueuedCallbackItem->ReentrancyCount = 0;
|
|
|
|
pQueuedCallbackItem->IoWorkItem = IoAllocateWorkItem(
|
|
DeviceObject
|
|
);
|
|
|
|
if (pQueuedCallbackItem->IoWorkItem == NULL) {
|
|
|
|
ExFreePool(pQueuedCallbackItem);
|
|
pQueuedCallbackItem = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(pCallbackHandle)) {
|
|
*pCallbackHandle = pQueuedCallbackItem;
|
|
}
|
|
|
|
if (pQueuedCallbackItem) {
|
|
|
|
pQueuedCallbackItem->QueuedCallback = CallbackRoutine;
|
|
pQueuedCallbackItem->DeviceObject = DeviceObject;
|
|
pQueuedCallbackItem->Context = Context;
|
|
pQueuedCallbackItem->Flags = Flags;
|
|
pQueuedCallbackItem->Irql = Irql;
|
|
pQueuedCallbackItem->Enqueued = 1;
|
|
|
|
if ((!(Flags&EQCF_DIFFERENT_THREAD_REQUIRED)) &&
|
|
(KeGetCurrentIrql() == Irql)&&
|
|
(pQueuedCallbackItem->ReentrancyCount < MAX_THREAD_REENTRANCY)) {
|
|
|
|
pQueuedCallbackItem->ReentrancyCount++;
|
|
EnqueuedIoWorkItemCallback(DeviceObject, (PVOID) pQueuedCallbackItem);
|
|
|
|
} else {
|
|
|
|
pQueuedCallbackItem->ReentrancyCount = 0;
|
|
|
|
if (Irql == PASSIVE_LEVEL) {
|
|
|
|
IoQueueWorkItem(
|
|
pQueuedCallbackItem->IoWorkItem,
|
|
EnqueuedIoWorkItemCallback,
|
|
DelayedWorkQueue,
|
|
pQueuedCallbackItem
|
|
);
|
|
|
|
} else {
|
|
|
|
ASSERT(Irql == DISPATCH_LEVEL);
|
|
KeInitializeDpc(
|
|
&pQueuedCallbackItem->Dpc,
|
|
EnqueuedDpcCallback,
|
|
pQueuedCallbackItem
|
|
);
|
|
|
|
KeInsertQueueDpc(
|
|
&pQueuedCallbackItem->Dpc,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
CallbackCancel(
|
|
IN PVOID pCallbackHandle
|
|
)
|
|
{
|
|
PQUEUED_CALLBACK_ITEM pQueuedCallbackItem;
|
|
|
|
pQueuedCallbackItem = (PQUEUED_CALLBACK_ITEM) pCallbackHandle;
|
|
|
|
if (InterlockedExchange(&pQueuedCallbackItem->Enqueued, 0) == 1) {
|
|
|
|
//
|
|
// We got it. If it's DPC, also try to yank it from the queue.
|
|
//
|
|
if (pQueuedCallbackItem->Irql == DISPATCH_LEVEL) {
|
|
|
|
KeRemoveQueueDpc(&pQueuedCallbackItem->Dpc);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
} else {
|
|
|
|
//
|
|
// Caller beat us to it...
|
|
//
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
CallbackFree(
|
|
IN PVOID pCallbackHandle
|
|
)
|
|
{
|
|
PQUEUED_CALLBACK_ITEM pQueuedCallbackItem;
|
|
|
|
pQueuedCallbackItem = (PQUEUED_CALLBACK_ITEM) pCallbackHandle;
|
|
|
|
ASSERT(pQueuedCallbackItem->Enqueued == 0);
|
|
|
|
IoFreeWorkItem(pQueuedCallbackItem->IoWorkItem);
|
|
ExFreePool(pQueuedCallbackItem);
|
|
}
|
|
|
|
VOID
|
|
EnqueuedDpcCallback(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
{
|
|
PQUEUED_CALLBACK_ITEM pQueuedCallbackItem;
|
|
QUEUED_CALLBACK_RETURN returnValue;
|
|
NTSTATUS ntStatus;
|
|
|
|
pQueuedCallbackItem = (PQUEUED_CALLBACK_ITEM) DeferredContext;
|
|
|
|
if (InterlockedExchange(&pQueuedCallbackItem->Enqueued, 0) == 1) {
|
|
|
|
returnValue = pQueuedCallbackItem->QueuedCallback(
|
|
pQueuedCallbackItem->DeviceObject,
|
|
pQueuedCallbackItem->Context
|
|
);
|
|
|
|
} else {
|
|
|
|
returnValue = QUEUED_CALLBACK_RETAIN;
|
|
}
|
|
|
|
switch(returnValue) {
|
|
|
|
case QUEUED_CALLBACK_FREE:
|
|
|
|
CallbackFree((PVOID) pQueuedCallbackItem);
|
|
break;
|
|
|
|
case QUEUED_CALLBACK_RETAIN:
|
|
|
|
//
|
|
// Nothing to do in this case, in fact we don't dare touch anything
|
|
// in the structure lest it be already freed.
|
|
//
|
|
break;
|
|
|
|
case QUEUED_CALLBACK_REISSUE:
|
|
|
|
//
|
|
// Re-enqueue it with the same handle to avoid reallocation.
|
|
//
|
|
ntStatus = CallbackEnqueue(
|
|
(PVOID *) &pQueuedCallbackItem,
|
|
pQueuedCallbackItem->QueuedCallback,
|
|
pQueuedCallbackItem->DeviceObject,
|
|
pQueuedCallbackItem->Context,
|
|
pQueuedCallbackItem->Irql,
|
|
pQueuedCallbackItem->Flags | EQCF_REUSE_HANDLE
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(ntStatus));
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
VOID
|
|
EnqueuedIoWorkItemCallback(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PQUEUED_CALLBACK_ITEM pQueuedCallbackItem;
|
|
QUEUED_CALLBACK_RETURN returnValue;
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
pQueuedCallbackItem = (PQUEUED_CALLBACK_ITEM) Context;
|
|
|
|
ASSERT(pQueuedCallbackItem->DeviceObject == DeviceObject);
|
|
|
|
if (InterlockedExchange(&pQueuedCallbackItem->Enqueued, 0) == 1) {
|
|
|
|
returnValue = pQueuedCallbackItem->QueuedCallback(
|
|
pQueuedCallbackItem->DeviceObject,
|
|
pQueuedCallbackItem->Context
|
|
);
|
|
|
|
} else {
|
|
|
|
returnValue = QUEUED_CALLBACK_RETAIN;
|
|
}
|
|
|
|
switch(returnValue) {
|
|
|
|
case QUEUED_CALLBACK_FREE:
|
|
|
|
CallbackFree((PVOID) pQueuedCallbackItem);
|
|
break;
|
|
|
|
case QUEUED_CALLBACK_RETAIN:
|
|
|
|
//
|
|
// Nothing to do in this case, in fact we don't dare touch anything
|
|
// in the structure lest it be already freed.
|
|
//
|
|
break;
|
|
|
|
case QUEUED_CALLBACK_REISSUE:
|
|
|
|
//
|
|
// Re-enqueue it with the same handle to avoid reallocation.
|
|
//
|
|
ntStatus = CallbackEnqueue(
|
|
(PVOID *) &pQueuedCallbackItem,
|
|
pQueuedCallbackItem->QueuedCallback,
|
|
pQueuedCallbackItem->DeviceObject,
|
|
pQueuedCallbackItem->Context,
|
|
pQueuedCallbackItem->Irql,
|
|
pQueuedCallbackItem->Flags | EQCF_REUSE_HANDLE
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(ntStatus));
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|