[WDF] Add Windows Driver Framework files

Takern from Microsoft GitHub repo:
d9c6040fe9

Licensed under MIT
This commit is contained in:
Victor Perevertkin 2020-09-24 23:51:15 +03:00
parent 545df81502
commit 8a978a179f
No known key found for this signature in database
GPG key ID: C750B7222E9C7830
475 changed files with 285099 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,18 @@
/*++
Copyright (c) Microsoft Corporation
ModuleName:
MxDeviceObjectUm.h
Abstract:
User Mode implementation of Device Object defined in MxDeviceObject.h
--*/
#pragma once
#include "MxDeviceObject.h"

View file

@ -0,0 +1,24 @@
/*++
Copyright (c) Microsoft Corporation
ModuleName:
MxDriverObjectUm.h
Abstract:
User Mode implementation of Driver Object defined in MxDriverObject.h
--*/
#pragma once
typedef PDRIVER_OBJECT_UM MdDriverObject;
typedef DRIVER_ADD_DEVICE_UM MdDriverAddDeviceType, *MdDriverAddDevice;
typedef DRIVER_UNLOAD_UM MdDriverUnloadType, *MdDriverUnload;
typedef DRIVER_DISPATCH_UM MdDriverDispatchType, *MdDriverDispatch;
#include "MxDriverObject.h"

View file

@ -0,0 +1,289 @@
/*++
Copyright (c) Microsoft Corporation
ModuleName:
MxEventUm.h
Abstract:
User mode implementation of event
class defined in MxEvent.h
Author:
Revision History:
--*/
#pragma once
typedef struct {
HANDLE Event;
#if DBG
EVENT_TYPE Type; //tracked to allow ReadState only for notification events
#endif
} MdEvent;
#include "DbgMacros.h"
#include "MxEvent.h"
__inline
MxEvent::MxEvent()
{
CLEAR_DBGFLAG_INITIALIZED;
m_Event.Event = NULL;
}
__inline
MxEvent::~MxEvent()
{
//
// PLEASE NOTE: shared code must not rely of d'tor uninitializing the
// event. d'tor may not be invoked if the event is used in a structure
// which is allocated/deallocated using MxPoolAllocate/Free instead of
// new/delete
//
Uninitialize();
}
_Must_inspect_result_
__inline
NTSTATUS
MxEvent::Initialize(
__in EVENT_TYPE Type,
__in BOOLEAN InitialState
)
{
NTSTATUS status = STATUS_SUCCESS;
HANDLE event;
BOOL bManualReset;
if (NotificationEvent == Type)
{
bManualReset = TRUE;
}
else
{
bManualReset = FALSE;
}
event = CreateEvent(
NULL,
bManualReset,
InitialState ? TRUE : FALSE,
NULL
);
if (NULL == event) {
DWORD err = GetLastError();
status = WinErrorToNtStatus(err);
goto exit;
}
m_Event.Event = event;
#if DBG
m_Event.Type = Type;
#endif
SET_DBGFLAG_INITIALIZED;
exit:
return status;
}
__inline
PVOID
MxEvent::GetEvent(
)
{
ASSERT_DBGFLAG_INITIALIZED;
return m_Event.Event;
}
__inline
VOID
MxEvent::Set(
)
{
ASSERT_DBGFLAG_INITIALIZED;
SetEvent(m_Event.Event);
}
__inline
VOID
MxEvent::SetWithIncrement(
__in KPRIORITY Priority
)
{
UNREFERENCED_PARAMETER(Priority);
ASSERT_DBGFLAG_INITIALIZED;
Set();
}
__inline
VOID
MxEvent::Clear(
)
{
ASSERT_DBGFLAG_INITIALIZED;
ResetEvent(m_Event.Event);
}
__drv_when(Timeout != NULL, _Must_inspect_result_)
__inline
NTSTATUS
MxEvent::WaitFor(
__in KWAIT_REASON WaitReason,
__in KPROCESSOR_MODE WaitMode,
__in BOOLEAN Alertable,
__in_opt PLARGE_INTEGER Timeout
)
/*++
Routine Description:
Waits for the event
Arguments:
WaitReason - Unused (only there to match km definition)
WaitMode - Unuses (only there to match km definition)
Altertable - Whether the wait is alertable
Timout - Timeout in 100 ns units, MUST BE NEGATIVE
(negative implies relative timeout)
Return Value:
Status corresponding to return value of WaitForSingleObjectEx
--*/
{
ASSERT_DBGFLAG_INITIALIZED;
DWORD retVal;
UNREFERENCED_PARAMETER(WaitReason);
UNREFERENCED_PARAMETER(WaitMode);
LONGLONG relativeTimeOut = 0;
LONGLONG timeoutInMs = 0;
DWORD dwTimeout = 0;
if (NULL != Timeout)
{
//
// Make sure that timeout is 0 or -ve (which implies relative timeout)
//
if (Timeout->QuadPart > 0)
{
Mx::MxAssertMsg(
"Absolute wait not supported in user mode",
FALSE
);
return STATUS_INVALID_PARAMETER;
}
//
// Remove the -ve sign
//
if (Timeout->QuadPart < 0)
{
relativeTimeOut = -1 * Timeout->QuadPart;
}
//
// Convert from 100ns units to milliseconds
//
timeoutInMs = (relativeTimeOut / (10 * 1000));
if (timeoutInMs > ULONG_MAX)
{
Mx::MxAssertMsg("Timeout too large", FALSE);
return STATUS_INVALID_PARAMETER;
}
else
{
dwTimeout = (DWORD) timeoutInMs;
}
}
retVal = WaitForSingleObjectEx(
m_Event.Event,
(NULL == Timeout) ? INFINITE : dwTimeout,
Alertable
);
switch(retVal)
{
case WAIT_ABANDONED:
return STATUS_ABANDONED;
case WAIT_OBJECT_0:
return STATUS_SUCCESS;
case WAIT_TIMEOUT:
return STATUS_TIMEOUT;
case WAIT_FAILED:
{
DWORD err = GetLastError();
return WinErrorToNtStatus(err);
}
default:
{
//
// We shoudn't get here
//
Mx::MxAssert(FALSE);
return STATUS_UNSUCCESSFUL;
}
}
}
LONG
__inline
MxEvent::ReadState(
)
{
ASSERT_DBGFLAG_INITIALIZED;
#if DBG
Mx::MxAssert(m_Event.Type == NotificationEvent);
#endif
if (WAIT_OBJECT_0 == WaitForSingleObject(
m_Event.Event,
0
)) {
return 1;
}
else {
return 0;
}
}
__inline
VOID
MxEvent::Uninitialize(
)
{
if (NULL != m_Event.Event) {
CloseHandle(m_Event.Event);
m_Event.Event = NULL;
}
CLEAR_DBGFLAG_INITIALIZED;
}

View file

@ -0,0 +1,89 @@
/*++
Copyright (c) Microsoft Corporation
ModuleName:
MxFileObjectUm.h
Abstract:
User Mode implementation of File Object defined in MxFileObject.h
--*/
#pragma once
struct IWudfFile;
typedef IWudfFile * MdFileObject;
#include "MxFileObject.h"
__inline
PLARGE_INTEGER
MxFileObject::GetCurrentByteOffset(
VOID
)
{
ASSERTMSG("Not implemented for UMDF\n", FALSE);
return NULL;
}
__inline
ULONG
MxFileObject::GetFlags(
VOID
)
{
return 0;
}
__inline
VOID
MxFileObject::SetFsContext(
_In_ PVOID Value
)
{
UNREFERENCED_PARAMETER(Value);
ASSERTMSG("Not implemented for UMDF\n", FALSE);
}
__inline
VOID
MxFileObject::SetFsContext2(
_In_ PVOID Value
)
{
UNREFERENCED_PARAMETER(Value);
ASSERTMSG("Not implemented for UMDF\n", FALSE);
}
__inline
PVOID
MxFileObject::GetFsContext(
VOID
)
{
ASSERTMSG("Not implemented for UMDF\n", FALSE);
return NULL;
}
__inline
PVOID
MxFileObject::GetFsContext2(
VOID
)
{
ASSERTMSG("Not implemented for UMDF\n", FALSE);
return NULL;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,185 @@
/*++
Copyright (c) Microsoft Corporation
ModuleName:
MxLockUm.h
Abstract:
User mode implementation of lock
class defined in MxLock.h
Author:
Revision History:
--*/
#pragma once
typedef struct {
CRITICAL_SECTION Lock;
bool Initialized;
DWORD OwnerThreadId;
} MdLock;
#include "DbgMacros.h"
#include "MxLock.h"
__inline
MxLock::MxLock(
)
{
CLEAR_DBGFLAG_INITIALIZED;
m_Lock.Initialized = false;
m_Lock.OwnerThreadId = 0;
MxLock::Initialize();
}
__inline
VOID
MxLockNoDynam::Initialize(
)
{
BOOL ret;
ASSERT_DBGFLAG_NOT_INITIALIZED;
ret = InitializeCriticalSectionAndSpinCount(&m_Lock.Lock, 0);
//
// InitializeCriticalSectionAndSpinCount always returns TRUE on Vista+
// Assert this contract on checked builds using DBGFLAG macro.
//
if (ret) {
m_Lock.Initialized = true;
SET_DBGFLAG_INITIALIZED;
}
ASSERT_DBGFLAG_INITIALIZED;
}
__inline
VOID
#pragma prefast(suppress:__WARNING_UNMATCHED_DECL_ANNO, "Can't apply kernel mode annotations.");
MxLockNoDynam::Acquire(
__out KIRQL * OldIrql
)
{
ASSERT_DBGFLAG_INITIALIZED;
EnterCriticalSection(&m_Lock.Lock);
DWORD threadId = GetCurrentThreadId();
if (threadId == m_Lock.OwnerThreadId) {
Mx::MxAssertMsg("Recursive acquision of the lock is not allowed", FALSE);
}
m_Lock.OwnerThreadId = threadId;
*OldIrql = PASSIVE_LEVEL;
}
__inline
BOOLEAN
MxLockNoDynam::TryToAcquire(
VOID
)
{
BOOLEAN acquired;
ASSERT_DBGFLAG_INITIALIZED;
acquired = (BOOLEAN) TryEnterCriticalSection(&m_Lock.Lock);
if (acquired) {
DWORD threadId = GetCurrentThreadId();
if (threadId == m_Lock.OwnerThreadId) {
Mx::MxAssertMsg("Recursive acquision of the lock is not allowed", FALSE);
}
m_Lock.OwnerThreadId = threadId;
}
return acquired;
}
__inline
VOID
#pragma prefast(suppress:__WARNING_UNMATCHED_DEFN, "Can't apply kernel mode annotations.");
MxLockNoDynam::AcquireAtDpcLevel(
)
{
ASSERT_DBGFLAG_INITIALIZED;
KIRQL dontCare;
Acquire(&dontCare);
}
__inline
VOID
#pragma prefast(suppress:__WARNING_UNMATCHED_DEFN, "Can't apply kernel mode annotations.");
MxLockNoDynam::Release(
KIRQL NewIrql
)
{
ASSERT_DBGFLAG_INITIALIZED;
Mx::MxAssert(NewIrql == PASSIVE_LEVEL);
m_Lock.OwnerThreadId = 0;
LeaveCriticalSection(&m_Lock.Lock);
}
__inline
VOID
#pragma prefast(suppress:__WARNING_UNMATCHED_DEFN, "Can't apply kernel mode annotations.");
MxLockNoDynam::ReleaseFromDpcLevel(
)
{
ASSERT_DBGFLAG_INITIALIZED;
Release(PASSIVE_LEVEL);
}
__inline
VOID
MxLockNoDynam::Uninitialize(
)
{
ASSERT_DBGFLAG_INITIALIZED;
DeleteCriticalSection(&m_Lock.Lock);
m_Lock.Initialized = false;
CLEAR_DBGFLAG_INITIALIZED;
}
__inline
MxLock::~MxLock(
)
{
//
// PLEASE NOTE: shared code must not rely of d'tor uninitializing the
// lock. d'tor may not be invoked if the event is used in a structure
// which is allocated/deallocated using MxPoolAllocate/Free instead of
// new/delete
//
if (m_Lock.Initialized) {
this->Uninitialize();
}
}

View file

@ -0,0 +1,58 @@
/*++
Copyright (c) Microsoft Corporation
ModuleName:
MxMemoryUm.h
Abstract:
User mode implementation of memory
class defined in MxMemory.h
Author:
Revision History:
--*/
#pragma once
#include "MxMemory.h"
__inline
PVOID
MxMemory::MxAllocatePoolWithTag(
__in POOL_TYPE PoolType,
__in SIZE_T NumberOfBytes,
__in ULONG Tag
)
{
UNREFERENCED_PARAMETER(PoolType);
UNREFERENCED_PARAMETER(Tag);
return ::HeapAlloc(
GetProcessHeap(),
0,
NumberOfBytes
);
}
__inline
VOID
MxMemory::MxFreePool(
__in PVOID Ptr
)
{
::HeapFree(
GetProcessHeap(),
0,
Ptr
);
}

View file

@ -0,0 +1,129 @@
/*++
Copyright (c) Microsoft Corporation
ModuleName:
MxPagedLockUm.h
Abstract:
User mode implementation of paged lock defined in
MxPagedLock.h
Author:
Revision History:
--*/
#pragma once
typedef struct {
CRITICAL_SECTION Lock;
bool Initialized;
DWORD OwnerThreadId;
} MdPagedLock;
#include "MxPagedLock.h"
__inline
MxPagedLock::MxPagedLock(
)
{
m_Lock.Initialized = false;
m_Lock.OwnerThreadId = 0;
}
_Must_inspect_result_
__inline
NTSTATUS
MxPagedLockNoDynam::Initialize(
)
{
if (InitializeCriticalSectionAndSpinCount(&m_Lock.Lock, 0)) {
m_Lock.Initialized = true;
return S_OK;
}
else {
DWORD err = GetLastError();
return WinErrorToNtStatus(err);
}
}
__inline
VOID
#pragma prefast(suppress:__WARNING_UNMATCHED_DEFN, "Can't apply kernel mode annotations.");
MxPagedLockNoDynam::Acquire(
)
{
EnterCriticalSection(&m_Lock.Lock);
DWORD threadId = GetCurrentThreadId();
if (threadId == m_Lock.OwnerThreadId) {
Mx::MxAssertMsg("Recursive acquision of the lock is not allowed", FALSE);
}
m_Lock.OwnerThreadId = GetCurrentThreadId();
}
__inline
VOID
MxPagedLockNoDynam::AcquireUnsafe(
)
{
MxPagedLockNoDynam::Acquire();
}
__inline
BOOLEAN
#pragma prefast(suppress:__WARNING_UNMATCHED_DEFN, "Can't apply kernel mode annotations.");
MxPagedLockNoDynam::TryToAcquire(
)
{
return TryEnterCriticalSection(&m_Lock.Lock) == TRUE ? TRUE : FALSE;
}
__inline
VOID
#pragma prefast(suppress:__WARNING_UNMATCHED_DEFN, "Can't apply kernel mode annotations.");
MxPagedLockNoDynam::Release(
)
{
m_Lock.OwnerThreadId = 0;
LeaveCriticalSection(&m_Lock.Lock);
}
__inline
VOID
MxPagedLockNoDynam::ReleaseUnsafe(
)
{
MxPagedLockNoDynam::Release();
}
__inline
VOID
MxPagedLockNoDynam::Uninitialize(
)
{
DeleteCriticalSection(&m_Lock.Lock);
m_Lock.Initialized = false;
}
__inline
MxPagedLock::~MxPagedLock(
)
{
if (m_Lock.Initialized) {
this->Uninitialize();
}
}

View file

@ -0,0 +1,479 @@
/*++
Copyright (c) Microsoft Corporation
ModuleName:
MxTimerUm.h
Abstract:
User mode implementation of timer defined in
MxTimer.h
Author:
Revision History:
--*/
#pragma once
typedef struct _MdTimer {
//
// Callback function to be invoked upon timer expiration and the context to
// be passed in to the callback function
//
MdDeferredRoutine m_TimerCallback;
PVOID m_TimerContext;
//
// The timer period
//
LONG m_Period;
//
// Handle to the timer object
//
PTP_TIMER m_TimerHandle;
//
// Work object to be executed by threadpool upon timer expiration
//
PTP_WORK m_WorkObject;
//
// Flag to indicate that the timer callback has started running
//
BOOL m_CallbackStartedRunning;
//
// Flag to indicate that the timer was started
// since it was created or since it was last stopped.
//
BOOL m_TimerWasStarted;
_MdTimer(
VOID
)
{
m_TimerHandle = NULL;
m_WorkObject = NULL;
m_TimerCallback = NULL;
m_TimerContext = NULL;
m_Period = 0;
m_CallbackStartedRunning = FALSE;
m_TimerWasStarted = FALSE;
}
~_MdTimer(
VOID
)
{
//
// Release the timer object
//
if (m_TimerHandle)
{
CloseThreadpoolTimer(m_TimerHandle);
m_TimerHandle = NULL;
}
//
// Release the work object
//
if (m_WorkObject)
{
CloseThreadpoolWork(m_WorkObject);
m_WorkObject = NULL;
}
}
BOOLEAN
IsInSystemQueue(
VOID
)
{
//
// Timer was not started since it was created or since
// it was last stopped, so it can't be in the system timer queue.
//
if (!m_TimerWasStarted) {
return FALSE;
}
//
// Periodic timers are always in the system timer queue.
//
if (m_Period != 0) {
return TRUE;
}
//
// Non-periodic timer:
//
// At this point, the timer callback function has either been canceled or
// has finished running. Examine the m_CallbackStartedRunning value to see
// which one of these happened.
//
if (m_CallbackStartedRunning)
{
//
// Timer cancellation was too late. Timer callback already started
// running and the timer was removed from the system timer queue.
//
return FALSE;
}
else
{
//
// Timer cancellation happened on time and prevented the timer callback
// from running.
//
return TRUE;
}
}
_Must_inspect_result_
NTSTATUS
Initialize(
__in_opt PVOID TimerContext,
__in MdDeferredRoutine TimerCallback,
__in LONG Period
)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
m_TimerCallback = TimerCallback;
m_TimerContext = TimerContext;
m_Period = Period;
//
// Create the timer object
//
m_TimerHandle = CreateThreadpoolTimer(_MdTimer::s_MdTimerCallback,
this,
NULL);
if (NULL == m_TimerHandle)
{
ntStatus = WinErrorToNtStatus(GetLastError());
}
//
// Create the work object
//
if (NT_SUCCESS(ntStatus))
{
m_WorkObject = CreateThreadpoolWork(_MdTimer::s_MdWorkCallback,
this,
NULL);
if (NULL == m_WorkObject)
{
ntStatus = WinErrorToNtStatus(GetLastError());
}
}
return ntStatus;
}
BOOLEAN
Start(
__in LARGE_INTEGER DueTime,
__in ULONG TolerableDelay
)
{
BOOLEAN bRetVal;
FILETIME dueFileTime;
if (m_TimerWasStarted) {
//
// Cancel the previously pended timer callback,
// we want it to execute after a full period elapsed.
//
WaitForThreadpoolTimerCallbacks(m_TimerHandle, TRUE);
}
//
// Return TRUE if the timer is in the system timer queue.
//
bRetVal = IsInSystemQueue();
//
// This is a fresh start for the timer, so clear the flag that the
// timer callback function may have previously set.
//
m_CallbackStartedRunning = FALSE;
//
// Set the timer started flag.
//
m_TimerWasStarted = TRUE;
//
// Copy the due time into a FILETIME structure
//
dueFileTime.dwLowDateTime = DueTime.LowPart;
dueFileTime.dwHighDateTime = (DWORD) DueTime.HighPart;
//
// Start the timer
//
SetThreadpoolTimer(m_TimerHandle,
&dueFileTime,
(DWORD) m_Period,
TolerableDelay);
return bRetVal;
}
_Must_inspect_result_
BOOLEAN
Stop(
VOID
)
{
BOOLEAN bRetVal;
bRetVal = IsInSystemQueue();
//
// Stop the timer
//
SetThreadpoolTimer(m_TimerHandle,
NULL, // pftDueTime
0, // msPeriod
0 // msWindowLength
);
//
// Cancel pending callbacks that have not yet started to execute and wait
// for outstanding callbacks to complete.
//
WaitForThreadpoolTimerCallbacks(m_TimerHandle,
TRUE // cancel pending callbacks
);
//
// Reset the timer started flag.
//
m_TimerWasStarted = FALSE;
return bRetVal;
}
VOID
TimerCallback(
VOID
)
{
//
// Invoke the user's callback function
//
m_TimerCallback(NULL, /* Reserved1 */
m_TimerContext,
NULL, /* Reserved2 */
NULL /* Reserved3 */
);
return;
}
static
VOID CALLBACK
s_MdWorkCallback(
__inout PTP_CALLBACK_INSTANCE Instance,
__inout_opt PVOID Context,
__inout PTP_WORK Work
)
{
struct _MdTimer *pThis = NULL;
UNREFERENCED_PARAMETER(Instance);
UNREFERENCED_PARAMETER(Work);
pThis = (struct _MdTimer*) Context;
pThis->TimerCallback();
return;
}
static
VOID CALLBACK
s_MdTimerCallback(
__inout PTP_CALLBACK_INSTANCE Instance,
__inout_opt PVOID Context,
__inout PTP_TIMER Timer
)
{
struct _MdTimer *pThis = NULL;
UNREFERENCED_PARAMETER(Instance);
UNREFERENCED_PARAMETER(Timer);
pThis = (struct _MdTimer*) Context;
//
// First, indicate that the callback has started running
//
pThis->m_CallbackStartedRunning = TRUE;
//
// Post a work object to execute the callback function supplied by the
// user of MxTimer.
//
// We do not execute the user-supplied callback here because we could
// run into a deadlock if the user is trying to cancel the timer by
// calling MxTimer::Stop. MxTimer::Stop actually blocks waiting for
// MdTimer::s_MdTimerCallback to finish executing, so that it can know
// where its attempt to cancel the timer was successful. If we were to
// execute the user's callback in MdTimer::s_MdTimerCallback, the user
// would have to be careful not to call MxTimer::Stop while holding a
// lock that the user's callback tries to acquire. In order to avoid
// imposing such a restriction on the user, we allow
// MdTimer::s_MdTimerCallback to return immediately after posting a
// work object to run the user's callback.
//
SubmitThreadpoolWork(pThis->m_WorkObject);
return;
}
BOOLEAN
StartWithReturn(
__in LARGE_INTEGER DueTime,
__in ULONG TolerableDelay
)
{
return Start(DueTime, TolerableDelay);
}
} MdTimer;
#include "MxTimer.h"
//
// Implementation of MxTimer functions
//
MxTimer::MxTimer(
VOID
)
{
}
MxTimer::~MxTimer(
VOID
)
{
}
_Must_inspect_result_
NTSTATUS
MxTimer::Initialize(
__in_opt PVOID TimerContext,
__in MdDeferredRoutine TimerCallback,
__in LONG Period
)
/*++
Routine description:
Initializes the MxTimer object.
Arguments:
TimerContext - Context information that will be passed in to the timer
callback function.
TimerCallback - The timer callback function.
*** IMPORTANT NOTE ***
MxTimer object must not be freed inside the timer callback function
because in the pre-Vista, user mode implementation of MxTimer, the
destructor blocks waiting for all callback functions to finish
executing. Hence freeing the MxTimer object inside the callback
function will result in a deadlock.
Period - The period of the timer in milliseconds.
Return value:
An NTSTATUS value that indicates whether or not we succeeded in
initializing the MxTimer
--*/
{
NTSTATUS ntStatus;
ntStatus = m_Timer.Initialize(TimerContext,
TimerCallback,
Period);
return ntStatus;
}
_Must_inspect_result_
NTSTATUS
MxTimer::InitializeEx(
__in_opt PVOID TimerContext,
__in MdExtCallback TimerCallback,
__in LONG Period,
__in ULONG TolerableDelay,
__in BOOLEAN UseHighResolutionTimer
)
{
UNREFERENCED_PARAMETER(TolerableDelay);
UNREFERENCED_PARAMETER(UseHighResolutionTimer);
UNREFERENCED_PARAMETER(TimerCallback);
UNREFERENCED_PARAMETER(TimerContext);
UNREFERENCED_PARAMETER(Period);
ASSERTMSG("Not implemented for UMDF\n", FALSE);
return STATUS_NOT_IMPLEMENTED;
}
VOID
MxTimer::Start(
__in LARGE_INTEGER DueTime,
__in ULONG TolerableDelay
)
{
m_Timer.Start(DueTime, TolerableDelay);
return;
}
_Must_inspect_result_
BOOLEAN
MxTimer::Stop(
VOID
)
{
BOOLEAN bRetVal;
bRetVal = m_Timer.Stop();
return bRetVal;
}
_Must_inspect_result_
BOOLEAN
MxTimer::StartWithReturn(
__in LARGE_INTEGER DueTime,
__in ULONG TolerableDelay
)
{
BOOLEAN bRetVal = TRUE;
bRetVal = m_Timer.StartWithReturn(DueTime, TolerableDelay);
return bRetVal;
}
VOID
MxTimer::FlushQueuedDpcs(
VOID
)
{
WaitForThreadpoolWorkCallbacks(m_Timer.m_WorkObject,
TRUE // cancel pending callbacks
);
}

View file

@ -0,0 +1,248 @@
/*++
Copyright (c) Microsoft Corporation
ModuleName:
MxUm.h
Abstract:
This file includes standard NT headers and
user mode versions of mode agnostic headers
It also contains definitions pulled out from wdm.h
Author:
Revision History:
--*/
#pragma once
#ifndef UMDF_USING_NTSTATUS
#define UMDF_USING_NTSTATUS
#endif
#include <windows.h>
#include <devpropdef.h>
#include <winioctl.h>
#ifdef UMDF_INFRASTRUCTURE
#ifndef WUDF_KERNEL
typedef PVOID PIRP;
typedef PVOID PIO_REMOVE_LOCK;
#endif
#endif
#include "wdmdefs.h"
#define WDF_VIOLATION ((ULONG)0x0000010DL)
#define FX_PLUGPLAY_REGKEY_DEVICEMAP 0x8
//
// Define the callback function to be supplied by a user-mode user of MxTimer
// It has the extra parameters Reserved1, Reserved2 and Reserved3 to make it
// look like the KDEFERRED_ROUTINE that used as the callback function for the
// kernel mode version of MxTimer. The user-mode user of MxTimer should not
// use these ReservedX parameters.
//
typedef
VOID
TIMER_CALLBACK_ROUTINE(
__in PKDPC Reserved1,
__in_opt PVOID Context,
__in_opt PVOID Reserved2,
__in_opt PVOID Reserved3
);
typedef PVOID PEX_TIMER;
typedef
VOID
TIMER_CALLBACK_ROUTINE_EX(
__in PEX_TIMER Reserved1,
__in_opt PVOID Context
);
typedef TIMER_CALLBACK_ROUTINE MdDeferredRoutineType, *MdDeferredRoutine;
typedef TIMER_CALLBACK_ROUTINE_EX MdExtCallbackType, *MdExtCallback;
//
// Forward defines
//
struct IFxMessageDispatch;
struct IUnknown;
struct IWudfIrp;
struct IWudfIoIrp;
struct IWudfFile;
struct IWDFObject;
struct IObjectCleanup;
struct IWudfDeviceStack;
struct IWudfDeviceStack2;
struct IWudfTargetCallbackDeviceChange;
struct IWudfIoDispatcher;
struct IWudfRemoteDispatcher;
struct IWudfDevice;
struct IWudfDevice2;
struct IWudfHost;
struct IWudfHost2;
//
// typedefs
//
typedef IWudfDevice * MdDeviceObject;
typedef IWudfIrp* MdIrp;
typedef LPCSTR MxFuncName;
typedef PVOID MxThread;
typedef PVOID MdEThread;
typedef PWUDF_IO_REMOVE_LOCK MdRemoveLock;
typedef PVOID MdInterrupt;
typedef struct _STACK_DEVICE_CAPABILITIES *PSTACK_DEVICE_CAPABILITIES;
typedef UINT64 WUDF_INTERFACE_CONTEXT;
typedef enum _WDF_REQUEST_TYPE WDF_REQUEST_TYPE;
typedef struct _WDF_INTERRUPT_INFO *PWDF_INTERRUPT_INFO;
typedef enum _WDF_INTERRUPT_POLICY WDF_INTERRUPT_POLICY;
typedef enum _WDF_INTERRUPT_PRIORITY WDF_INTERRUPT_PRIORITY;
typedef struct _WDF_OBJECT_ATTRIBUTES *PWDF_OBJECT_ATTRIBUTES;
typedef enum _WDF_DEVICE_IO_BUFFER_RETRIEVAL WDF_DEVICE_IO_BUFFER_RETRIEVAL;
typedef enum RdWmiPowerAction;
typedef struct _WDF_REQUEST_PARAMETERS *PWDF_REQUEST_PARAMETERS;
typedef enum _WDF_EVENT_TYPE WDF_EVENT_TYPE;
typedef enum _WDF_FILE_INFORMATION_CLASS WDF_FILE_INFORMATION_CLASS;
typedef WDF_FILE_INFORMATION_CLASS *PWDF_FILE_INFORMATION_CLASS;
typedef
NTSTATUS
WUDF_IO_COMPLETION_ROUTINE (
__in MdDeviceObject DeviceObject,
__in MdIrp Irp,
__in PVOID Context
);
typedef WUDF_IO_COMPLETION_ROUTINE *PWUDF_IO_COMPLETION_ROUTINE;
typedef
VOID
WUDF_DRIVER_CANCEL (
__in MdDeviceObject DeviceObject,
__in MdIrp Irp
);
typedef WUDF_DRIVER_CANCEL *PWUDF_DRIVER_CANCEL;
typedef WUDF_IO_COMPLETION_ROUTINE MdCompletionRoutineType, *MdCompletionRoutine;
typedef WUDF_DRIVER_CANCEL MdCancelRoutineType, *MdCancelRoutine;
//
// From wdm.h
//
typedef
__drv_functionClass(REQUEST_POWER_COMPLETE)
__drv_sameIRQL
VOID
REQUEST_POWER_COMPLETE (
__in MdDeviceObject DeviceObject,
__in UCHAR MinorFunction,
__in POWER_STATE PowerState,
__in_opt PVOID Context,
__in PIO_STATUS_BLOCK IoStatus
);
typedef REQUEST_POWER_COMPLETE *PREQUEST_POWER_COMPLETE;
typedef REQUEST_POWER_COMPLETE MdRequestPowerCompleteType, *MdRequestPowerComplete;
typedef enum _WDF_DEVICE_IO_TYPE WDF_DEVICE_IO_TYPE;
typedef struct _DRIVER_OBJECT_UM *PDRIVER_OBJECT_UM;
//
// Driver object's basic interface.
//
typedef
NTSTATUS
DRIVER_ADD_DEVICE_UM (
_In_ PDRIVER_OBJECT_UM DriverObject,
_In_ PVOID Context,
_In_ IWudfDeviceStack * DevStack,
_In_ LPCWSTR KernelDeviceName,
_In_opt_ HKEY hPdoKey,
_In_ LPCWSTR pwszServiceName,
_In_ LPCWSTR pwszDevInstanceID,
_In_ ULONG ulDriverID
);
typedef DRIVER_ADD_DEVICE_UM *PFN_DRIVER_ADD_DEVICE_UM;
typedef
VOID
DRIVER_DISPATCH_UM (
_In_ IWudfDevice * DeviceObject,
_In_ IWudfIrp * Irp,
_In_opt_ IUnknown * Context
);
typedef DRIVER_DISPATCH_UM *PFN_DRIVER_DISPATCH_UM;
typedef
VOID
DRIVER_UNLOAD_UM (
_In_ PDRIVER_OBJECT_UM DriverObject
);
typedef DRIVER_UNLOAD_UM *PFN_DRIVER_UNLOAD_UM;
#ifdef UMDF_INFRASTRUCTURE
#ifndef WUDF_KERNEL
typedef CCHAR KPROCESSOR_MODE;
typedef PVOID PMDL;
typedef
_IRQL_requires_same_
_Function_class_(ALLOCATE_FUNCTION)
PVOID
ALLOCATE_FUNCTION (
_In_ POOL_TYPE PoolType,
_In_ SIZE_T NumberOfBytes,
_In_ ULONG Tag
);
typedef ALLOCATE_FUNCTION *PALLOCATE_FUNCTION;
typedef
_IRQL_requires_same_
_Function_class_(FREE_FUNCTION)
VOID
FREE_FUNCTION (
_In_ __drv_freesMem(Mem) PVOID Buffer
);
typedef FREE_FUNCTION *PFREE_FUNCTION;
#endif
#endif
//===============================================================================
#include <limits.h>
#include <driverspecs.h>
#include "ErrToStatus.h"
#include "MxDriverObjectUm.h"
#include "MxDeviceObjectUm.h"
#include "MxFileObjectUm.h"
#include "MxGeneralUm.h"
#include "MxLockUm.h"
#include "MxPagedLockUm.h"
#include "MxEventUm.h"
#include "MxMemoryUm.h"
#include "MxTimerUm.h"
#include "MxWorkItemUm.h"

View file

@ -0,0 +1,294 @@
/*++
Copyright (c) Microsoft Corporation
ModuleName:
MxWorkItemUm.h
Abstract:
User mode implementation of work item
class defined in MxWorkItem.h
***********PLEASE NOTE*****************************
A significant difference from kernel mode implementation of work item
is that user mode version of MxWorkItem::_Free synchronously waits for
callback to return.
This implies that _Free cannot be invoked from within the callback otherwise
it would lead to a deadlock.
PLEASE NOTE that _Free cannot be made to return without waiting without
significant changes.
If Free is not made to wait synchronously there is a potential for binary
unload while workitem is running - even with waiting for an event/reference
count etc., the tail instructions may be running.
The only way to resolve that is to move work-item code out of framework
binary and into the host so that host can take a reference on framework
binary around the work item callback invocation (similar to the way I/O
manager keeps a reference on the device object around the invocation of
workitem callback).
****************************************************
Author:
Revision History:
--*/
#pragma once
typedef
VOID
MX_WORKITEM_ROUTINE (
__in MdDeviceObject DeviceObject,
__in_opt PVOID Context
);
typedef MX_WORKITEM_ROUTINE *PMX_WORKITEM_ROUTINE;
typedef struct {
MdDeviceObject DeviceObject;
//
// threadpool wait block
//
PTP_WAIT WaitBlock;
HANDLE WorkItemEvent;
PMX_WORKITEM_ROUTINE Callback;
PVOID Context;
//
// True if callbacks run in the default thread pool environment,
// rather than in an environment explicitly owned by the driver.
// This has implications in MxWorkItem::_Free.
//
BOOLEAN DefaultThreadpoolEnv;
} UmWorkItem;
typedef UmWorkItem* MdWorkItem;
#include "MxWorkItem.h"
__inline
MxWorkItem::MxWorkItem(
)
{
m_WorkItem = NULL;
}
_Must_inspect_result_
__inline
NTSTATUS
MxWorkItem::Allocate(
__in MdDeviceObject DeviceObject,
__in_opt PVOID ThreadPoolEnv
)
{
DWORD err = 0;
m_WorkItem = (MdWorkItem)::HeapAlloc(
GetProcessHeap(),
0,
sizeof(UmWorkItem)
);
if (NULL == m_WorkItem) {
return STATUS_INSUFFICIENT_RESOURCES;
}
ZeroMemory(m_WorkItem, sizeof(UmWorkItem));
m_WorkItem->WorkItemEvent = CreateEvent(
NULL,
FALSE,
FALSE,
NULL);
if (NULL == m_WorkItem->WorkItemEvent) {
err = GetLastError();
goto exit;
}
m_WorkItem->WaitBlock = CreateThreadpoolWait(
_WorkerThunk,
this->GetWorkItem(), // Context to callback function
(PTP_CALLBACK_ENVIRON)ThreadPoolEnv
);
if (m_WorkItem->WaitBlock == NULL) {
err = GetLastError();
goto exit;
}
m_WorkItem->DefaultThreadpoolEnv = (NULL == ThreadPoolEnv);
m_WorkItem->DeviceObject = DeviceObject;
exit:
//
// Cleanup in case of failure
//
if (0 != err) {
if (NULL != m_WorkItem->WorkItemEvent) {
CloseHandle(m_WorkItem->WorkItemEvent);
m_WorkItem->WorkItemEvent = NULL;
}
::HeapFree(GetProcessHeap(), 0, m_WorkItem);
m_WorkItem = NULL;
}
return NTSTATUS_FROM_WIN32(err);
}
__inline
VOID
MxWorkItem::Enqueue(
__in PMX_WORKITEM_ROUTINE Callback,
__in PVOID Context
)
{
//
// ASSUMPTION: This function assumes that another call to Enqueue
// is made only after the callback has been invoked, altough it is OK
// to make another call from within the callback.
//
// It is up to a higher layer/caller to ensure this.
// For example: FxSystemWorkItem layered on top of MxWorkItem ensures this.
//
//
// Since multiple calls to Enqueue cannot be made at the same time
// as explained above, it is OK to store callback and context in
// the workitem itself.
//
// This behavior is similar to that of IoQueueWorkItem which accepts
// a callback and a context which are stored within the work-item.
//
m_WorkItem->Callback = Callback;
m_WorkItem->Context = Context;
//
// We must register the event with the wait object before signaling it
// to trigger the wait callback.
//
SetThreadpoolWait(m_WorkItem->WaitBlock,
m_WorkItem->WorkItemEvent,
NULL // timeout
);
SetEvent(m_WorkItem->WorkItemEvent);
}
__inline
MdWorkItem
MxWorkItem::GetWorkItem(
)
{
return m_WorkItem;
}
__inline
VOID
MxWorkItem::_Free(
__in MdWorkItem Item
)
{
//
// PLEASE NOTE that _Free waits for callback to return synchronously.
//
// DO NOT call _Free from work item callback otherwise it would cause a
// deadlock.
//
// Please see comments on the top of the file.
//
if (NULL != Item) {
//
// Wait indefinitely for work item to complete
//
if (NULL != Item->WaitBlock) {
//
// this will prevent any new waits to be queued but callbacks
// already queued will still occur.
//
SetThreadpoolWait(Item->WaitBlock, NULL, NULL);
//
// If the callbacks ran in the default thread pool environment,
// wait for callbacks to finish.
// If they ran in an environment explicitly owned by the driver,
// then this wait will happen before the driver DLL is unloaded,
// the host takes care of this.
//
if (Item->DefaultThreadpoolEnv) {
WaitForThreadpoolWaitCallbacks(Item->WaitBlock,
FALSE // donot cancel pending waits
);
}
//
// Release the wait object.
//
CloseThreadpoolWait(Item->WaitBlock);
}
if (NULL != Item->WorkItemEvent) {
CloseHandle(Item->WorkItemEvent);
Item->WorkItemEvent = NULL;
}
::HeapFree(
GetProcessHeap(),
0,
Item
);
}
}
__inline
VOID
MxWorkItem::Free(
)
{
//
// PLEASE NOTE that _Free waits for callback to return synchronously.
//
// DO NOT call Free from work item callback otherwise it would cause a
// deadlock.
//
// Please see comments on the top of the file.
//
if (NULL != m_WorkItem) {
MxWorkItem::_Free(m_WorkItem);
m_WorkItem = NULL;
}
}
//
// FxAutoWorkitem
//
__inline
MxAutoWorkItem::~MxAutoWorkItem(
)
{
this->Free();
}