[WDF] Add Windows Driver Framework files
[reactos.git] / sdk / lib / drivers / wdf / shared / inc / primitives / km / mxtimerkm.h
diff --git a/sdk/lib/drivers/wdf/shared/inc/primitives/km/mxtimerkm.h b/sdk/lib/drivers/wdf/shared/inc/primitives/km/mxtimerkm.h
new file mode 100644 (file)
index 0000000..3284b45
--- /dev/null
@@ -0,0 +1,291 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+ModuleName:
+
+    MxTimerKm.h
+
+Abstract:
+
+    Kernel mode implementation of timer defined in
+    MxTimer.h
+
+Author:
+
+
+Revision History:
+
+
+
+--*/
+
+#pragma once
+
+#define TolerableDelayUnlimited ((ULONG)-1)
+
+typedef
+BOOLEAN
+(*PFN_KE_SET_COALESCABLE_TIMER) (
+    __inout PKTIMER Timer,
+    __in LARGE_INTEGER DueTime,
+    __in ULONG Period,
+    __in ULONG TolerableDelay,
+    __in_opt PKDPC Dpc
+    );
+
+typedef struct _MdTimer {
+
+
+    //
+    // The timer period
+    //
+    LONG m_Period;
+
+    //
+    // Tracks whether the ex timer is being used
+    //
+    BOOLEAN m_IsExtTimer;
+
+#pragma warning(push)
+#pragma warning( disable: 4201 ) // nonstandard extension used : nameless struct/union
+
+    union {
+        struct {
+            //
+            // Callback function to be invoked upon timer expiration
+            //
+            MdDeferredRoutine m_TimerCallback;
+            KTIMER KernelTimer;
+            KDPC TimerDpc;
+        };
+
+        struct {
+            //
+            // Callback function to be invoked upon timer expiration
+            //
+            MdExtCallback m_ExTimerCallback;
+            PEX_TIMER m_KernelExTimer;
+        };
+    };
+
+#pragma warning(pop)
+
+    //
+    // Context to be passed in to the callback function
+    //
+    PVOID m_TimerContext;
+
+} MdTimer;
+
+#include "MxTimer.h"
+
+MxTimer::MxTimer(
+    VOID
+    )
+{
+    m_Timer.m_TimerContext = NULL;
+    m_Timer.m_TimerCallback = NULL;
+    m_Timer.m_Period = 0;
+    m_Timer.m_KernelExTimer = NULL;
+}
+
+MxTimer::~MxTimer(
+    VOID
+    )
+{
+    BOOLEAN wasCancelled;
+
+    if (m_Timer.m_IsExtTimer &&
+        m_Timer.m_KernelExTimer) {
+        wasCancelled = ExDeleteTimer(m_Timer.m_KernelExTimer,
+                                     TRUE, // Cancel if pending. We don't expect
+                                           // it to be pending though
+                                     FALSE,// Wait
+                                     NULL);
+        //
+        // Timer should not have been pending
+        //
+        ASSERT(wasCancelled == FALSE);
+        m_Timer.m_KernelExTimer = NULL;
+    }
+}
+
+NTSTATUS
+#pragma prefast(suppress:__WARNING_UNMATCHED_DECL_ANNO, "_Must_inspect_result_ not needed in kernel mode as the function always succeeds");
+MxTimer::Initialize(
+    __in_opt PVOID TimerContext,
+    __in MdDeferredRoutine TimerCallback,
+    __in LONG Period
+    )
+{
+    m_Timer.m_TimerContext = TimerContext;
+    m_Timer.m_TimerCallback = TimerCallback;
+    m_Timer.m_Period = Period;
+
+    KeInitializeTimerEx(&(m_Timer.KernelTimer), NotificationTimer);
+    KeInitializeDpc(&(m_Timer.TimerDpc), // Timer DPC
+                    m_Timer.m_TimerCallback, // DeferredRoutine
+                    m_Timer.m_TimerContext); // DeferredContext
+
+    m_Timer.m_IsExtTimer = FALSE;
+
+    return STATUS_SUCCESS;
+}
+
+_Must_inspect_result_
+NTSTATUS
+MxTimer::InitializeEx(
+    __in_opt PVOID TimerContext,
+    __in MdExtCallback TimerCallback,
+    __in LONG Period,
+    __in ULONG TolerableDelay,
+    __in BOOLEAN UseHighResolutionTimer
+    )
+/*++
+
+Routine Description:
+
+    Initializes an Ex timer. By invoking this routine instead of
+    Initialize, the client is implicitly declaring that it wants
+    to use the new timers
+
+Arguments:
+
+    TimerContext -          Context to be passed back with the cllback
+    TimerCallback -         Callback to be invoked when the timer fires
+    Period -                Period in ms
+    TolerableDelay -        Tolerable delay in ms
+    UseHighResolutionTimer- Indicates whether to use the high
+                            resolution timers
+
+Returns:
+
+    Status
+
+--*/
+
+{
+    NTSTATUS status;
+    ULONG attributes = 0;
+
+    m_Timer.m_TimerContext = TimerContext;
+    m_Timer.m_ExTimerCallback = TimerCallback;
+    m_Timer.m_Period = Period;
+
+    if (TolerableDelay != 0) {
+
+        attributes |= EX_TIMER_NO_WAKE;
+
+    } else if (UseHighResolutionTimer) {
+
+        attributes |= EX_TIMER_HIGH_RESOLUTION;
+    }
+
+    m_Timer.m_KernelExTimer = ExAllocateTimer(m_Timer.m_ExTimerCallback,
+                                              TimerContext,
+                                              attributes);
+    if (m_Timer.m_KernelExTimer) {
+
+        status = STATUS_SUCCESS;
+
+    } else {
+
+        status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    m_Timer.m_IsExtTimer = TRUE;
+
+    return status;
+}
+
+
+__inline
+BOOLEAN
+MxTimer::StartWithReturn(
+    __in LARGE_INTEGER DueTime,
+    __in ULONG TolerableDelay
+    )
+{
+    if (m_Timer.m_IsExtTimer) {
+
+        EXT_SET_PARAMETERS parameters;
+
+        ExInitializeSetTimerParameters(&parameters);
+
+        //
+        // We get the delay in ms but the underlying API needs it in 100 ns
+        // units. Convert tolerable delay from ms to 100 ns. However,
+        // MAXULONG (TolerableDelayUnlimited) has a special meaning that the
+        // system should never be woken up, so we assign the corresponding
+        // special value for Ex timers
+        //
+        if (TolerableDelay == TolerableDelayUnlimited) {
+            parameters.NoWakeTolerance = EX_TIMER_UNLIMITED_TOLERANCE;
+        } else {
+            parameters.NoWakeTolerance = ((LONGLONG) TolerableDelay) * 10 * 1000;
+        }
+
+        return ExSetTimer(m_Timer.m_KernelExTimer,
+                          DueTime.QuadPart,
+                          (((LONGLONG) m_Timer.m_Period) * 10 * 1000),
+                          &parameters);
+
+    } else {
+
+        return KeSetCoalescableTimer(&(m_Timer.KernelTimer),
+                                     DueTime,
+                                     m_Timer.m_Period,
+                                     TolerableDelay,
+                                     &(m_Timer.TimerDpc));
+    }
+
+}
+
+
+VOID
+MxTimer::Start(
+    __in LARGE_INTEGER DueTime,
+    __in ULONG TolerableDelay
+    )
+{
+    if (m_Timer.m_IsExtTimer) {
+
+        StartWithReturn(DueTime,TolerableDelay);
+
+    } else {
+        KeSetCoalescableTimer(&(m_Timer.KernelTimer),
+                              DueTime,
+                              m_Timer.m_Period,
+                              TolerableDelay,
+                              &(m_Timer.TimerDpc));
+    }
+
+    return;
+}
+
+_Must_inspect_result_
+BOOLEAN
+MxTimer::Stop(
+    VOID
+    )
+{
+    BOOLEAN bRetVal;
+
+    if (m_Timer.m_IsExtTimer) {
+        bRetVal = ExCancelTimer(m_Timer.m_KernelExTimer, NULL);
+
+    } else {
+        bRetVal = KeCancelTimer(&(m_Timer.KernelTimer));
+    }
+
+    return bRetVal;
+}
+
+VOID
+MxTimer::FlushQueuedDpcs(
+    VOID
+    )
+{
+    Mx::MxFlushQueuedDpcs();
+}