[WDF] Add Windows Driver Framework files
[reactos.git] / sdk / lib / drivers / wdf / shared / core / fxdriver.cpp
diff --git a/sdk/lib/drivers/wdf/shared/core/fxdriver.cpp b/sdk/lib/drivers/wdf/shared/core/fxdriver.cpp
new file mode 100644 (file)
index 0000000..1dc3bc7
--- /dev/null
@@ -0,0 +1,656 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+    FxDriver.cpp
+
+Abstract:
+
+    This is the main driver framework.
+
+Author:
+
+
+
+Environment:
+
+    Both kernel and user mode
+
+Revision History:
+
+--*/
+
+#include "coreprivshared.hpp"
+#include "fxiotarget.hpp"
+
+// Tracing support
+extern "C" {
+#include "FxDriver.tmh"
+}
+
+FxDriver::FxDriver(
+    __in MdDriverObject     ArgDriverObject,
+    __in PWDF_DRIVER_CONFIG DriverConfig,
+    __in PFX_DRIVER_GLOBALS FxDriverGlobals
+    ) :
+    FxNonPagedObject(FX_TYPE_DRIVER, sizeof(FxDriver), FxDriverGlobals),
+    m_DriverObject(ArgDriverObject),
+    m_CallbackMutexLock(FxDriverGlobals)
+{
+    RtlInitUnicodeString(&m_RegistryPath, NULL);
+
+
+
+
+
+
+#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
+    m_ExecutionLevel = WdfExecutionLevelDispatch;
+#else
+    m_ExecutionLevel = WdfExecutionLevelPassive;
+#endif
+
+    m_SynchronizationScope = WdfSynchronizationScopeNone;
+
+    m_CallbackLockPtr = NULL;
+    m_CallbackLockObjectPtr = NULL;
+
+    m_DisposeList = NULL;
+
+    //
+    // These are initialized up front so that sub objects
+    // can have the right configuration
+    //
+    WDF_DRIVER_CONFIG_INIT(&m_Config, NULL);
+
+    // Only copy the smallest of what is specified and our size
+    RtlCopyMemory(&m_Config, DriverConfig, min(sizeof(m_Config), DriverConfig->Size) );
+
+    m_DebuggerConnected = FALSE;
+
+#if FX_IS_USER_MODE
+    m_DriverParametersKey = NULL;
+#endif
+}
+
+FxDriver::~FxDriver()
+{
+    // Make it always present right now even on free builds
+    if (IsDisposed() == FALSE) {
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_FATAL, TRACINGDRIVER,
+            "FxDriver 0x%p not disposed: this maybe a driver reference count "
+            "problem with WDFDRIVER 0x%p", this, GetObjectHandleUnchecked());
+
+        FxVerifierBugCheck(GetDriverGlobals(),
+                           WDF_OBJECT_ERROR,
+                           (ULONG_PTR) GetObjectHandleUnchecked(),
+                           (ULONG_PTR) this);
+    }
+
+    //
+    // Free the memory for the registry path if required.
+    //
+    if (m_RegistryPath.Buffer) {
+        FxPoolFree(m_RegistryPath.Buffer);
+    }
+
+    if (m_DisposeList != NULL) {
+        m_DisposeList->DeleteObject();
+    }
+
+#if FX_IS_USER_MODE
+    //
+    // Close the R/W handle to the driver's service parameters key
+    // that we opened during Initialize.
+    //
+    if (m_DriverParametersKey != NULL) {
+        NTSTATUS status = FxRegKey::_Close(m_DriverParametersKey);
+        if (!NT_SUCCESS(status)) {
+            DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDRIVER,
+                                "Cannot close Driver Parameters key %!STATUS!",
+                                status);
+        }
+        m_DriverParametersKey = NULL;
+    }
+
+    //
+    // The host-created driver object holds a reference to this
+    // FxDriver object. Clear it, since this object was deleted.
+    //
+    ClearDriverObjectFxDriver();
+#endif
+}
+
+BOOLEAN
+FxDriver::Dispose(
+    VOID
+    )
+{
+    if (m_DisposeList != NULL) {
+        m_DisposeList->WaitForEmpty();
+    }
+
+    return __super::Dispose();
+}
+
+VOID
+FxDriver::Unload(
+    __in MdDriverObject DriverObject
+    )
+{
+    PFX_DRIVER_GLOBALS pFxDriverGlobals;
+    FxDriver *pDriver;
+
+    pDriver = FxDriver::GetFxDriver(DriverObject);
+    if (pDriver == NULL) {
+        return;
+    }
+
+    pFxDriverGlobals = pDriver->GetDriverGlobals();
+
+    DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDRIVER,
+                        "Unloading WDFDRIVER %p, PDRIVER_OBJECT_UM %p",
+                        pDriver->GetHandle(), DriverObject);
+    //
+    // Invoke the driver if they specified an unload routine.
+    //
+    if (pDriver->m_DriverUnload.Method) {
+        pDriver->m_DriverUnload.Invoke(pDriver->GetHandle());
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDRIVER,
+                            "Driver unload routine Exit WDFDRIVER %p, PDRIVER_OBJECT_UM %p",
+                            pDriver->GetHandle(), DriverObject);
+    }
+
+    //
+    // Delete the FxDriver object.
+    //
+    // This releases the FxDriver reference.  Must be called at PASSIVE
+    //
+    pDriver->DeleteObject();
+
+    pFxDriverGlobals->Driver = NULL;
+
+    FxDestroy(pFxDriverGlobals);
+}
+
+VOID
+FxDriver::_InitializeDriverName(
+    __in PFX_DRIVER_GLOBALS FxDriverGlobals,
+    __in PCUNICODE_STRING RegistryPath
+    )
+/*++
+
+Routine Description:
+    Create a CHAR version of the service name contained in RegistryPath.
+
+Arguments:
+    RegistryPath - the path to the service name in the registry.
+
+Return Value:
+    None
+
+  --*/
+{
+    PWCHAR pCur, pBegin, pEnd;
+
+    RtlZeroMemory(&FxDriverGlobals->Public.DriverName[0],
+                  sizeof(FxDriverGlobals->Public.DriverName) );
+
+    if (RegistryPath == NULL) {
+        return;
+    }
+
+    pBegin = RegistryPath->Buffer;
+
+    //
+    // pEnd is one past the end of the string, while pCur is a pointer to the
+    // last character of the string.  We will decrement pCur down towards the
+    // beginning of the string.
+    //
+    pEnd = pBegin + (RegistryPath->Length / sizeof(WCHAR));
+    pCur = pEnd - 1;
+
+    for ( ; *pCur != L'\\' && pCur != pBegin; pCur--) {
+        DO_NOTHING();
+    }
+
+    if (pCur != pBegin && *pCur == L'\\') {
+        size_t regLen;
+        ULONG i;
+
+        pCur++;
+
+        //
+        // Can't use wcslen becuase this is a UNICODE_STRING which means that it
+        // does not necessarily have a terminating NULL in the buffer.
+        //
+        regLen = pEnd - pCur;
+        if (regLen > WDF_DRIVER_GLOBALS_NAME_LEN-1) {
+            regLen = WDF_DRIVER_GLOBALS_NAME_LEN-1;
+        }
+
+
+        for (i = 0; i < regLen; i++) {
+            FxDriverGlobals->Public.DriverName[i] = (CHAR) pCur[i];
+        }
+    }
+    else {
+        NTSTATUS status;
+
+#if FX_CORE_MODE==FX_CORE_KERNEL_MODE
+        status = RtlStringCbCopyA(FxDriverGlobals->Public.DriverName,
+                                  sizeof(FxDriverGlobals->Public.DriverName),
+                                  "WDF");
+#else // USER_MODE
+        HRESULT hr;
+        hr = StringCbCopyA(FxDriverGlobals->Public.DriverName,
+                           sizeof(FxDriverGlobals->Public.DriverName),
+                           "WDF");
+        if (HRESULT_FACILITY(hr) == FACILITY_WIN32) {
+            status = WinErrorToNtStatus(HRESULT_CODE(hr));
+        }
+        else {
+            status = SUCCEEDED(hr) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
+        }
+#endif
+
+        UNREFERENCED_PARAMETER(status);
+        ASSERT(NT_SUCCESS(status));
+    }
+}
+
+VOID
+FxDriver::_InitializeTag(
+    __in PFX_DRIVER_GLOBALS FxDriverGlobals,
+    __in PWDF_DRIVER_CONFIG Config
+    )
+/*++
+
+Routine Description:
+    Tries to create a tag for the driver based off of its name.  This tag is
+    used for allocations made on behalf of the driver so that it is easier to
+    track which allocations belong to which image.
+
+Arguments:
+    Config - driver writer provided config.  in the future, may contain a tag
+             value in it
+
+Return Value:
+    None
+
+  --*/
+{
+    PCHAR  pBegin;
+    size_t length;
+
+    UNREFERENCED_PARAMETER(Config);
+
+    length = strlen(FxDriverGlobals->Public.DriverName);
+    pBegin = &FxDriverGlobals->Public.DriverName[0];
+
+    if (length >= 3) {
+        //
+        // If the driver name begins with "WDF" (case insensitive), start after
+        // "WDF"
+        //
+        if ((pBegin[0] == 'w' || pBegin[0] == 'W') &&
+            (pBegin[1] == 'd' || pBegin[1] == 'D') &&
+            (pBegin[2] == 'f' || pBegin[2] == 'F')) {
+            length -=3;
+            pBegin += 3;
+        }
+    }
+
+    if (length <= 2) {
+        //
+        // 2 or less characters is not a unique enough tag, just use the default
+        // tag.
+        //
+        FxDriverGlobals->Tag = FX_TAG;
+    }
+    else {
+
+        if (length > sizeof(ULONG)) {
+            length = sizeof(ULONG);
+        }
+
+        //
+        // This copies the bytes in the right order (so that they appear correct
+        // when dumped as a sequence of chars)
+        //
+        RtlCopyMemory(&FxDriverGlobals->Tag,
+                      pBegin,
+                      length);
+
+        FxDriverGlobals->Public.DriverTag = FxDriverGlobals->Tag;
+    }
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDriver::Initialize(
+    __in PCUNICODE_STRING ArgRegistryPath,
+    __in PWDF_DRIVER_CONFIG Config,
+    __in_opt PWDF_OBJECT_ATTRIBUTES DriverAttributes
+    )
+{
+    PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
+    NTSTATUS status;
+
+    // WDFDRIVER can not be deleted by the device driver
+    MarkNoDeleteDDI();
+
+    MarkDisposeOverride(ObjectDoNotLock);
+
+    //
+    // Configure Constraints
+    //
+    ConfigureConstraints(DriverAttributes);
+
+    if (m_DriverObject.GetObject() == NULL) {
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    // Allocate FxDisposeList
+    status = FxDisposeList::_Create(FxDriverGlobals, m_DriverObject.GetObject(), &m_DisposeList);
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    //
+    // Store FxDriver in Driver object extension
+    //
+    status = AllocateDriverObjectExtensionAndStoreFxDriver();
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    //
+    // Store away the callback functions.
+    //
+    if ((Config->DriverInitFlags & WdfDriverInitNoDispatchOverride) == 0) {
+        //
+        // Caller doesn't want to override the dispatch table.  That
+        // means that they want to create everything and still be in
+        // control (or at least the port driver will take over)
+        //
+        m_DriverDeviceAdd.Method  = Config->EvtDriverDeviceAdd;
+        m_DriverUnload.Method     = Config->EvtDriverUnload;
+    }
+
+    if (ArgRegistryPath != NULL) {
+        USHORT length;
+
+        length = ArgRegistryPath->Length + sizeof(UNICODE_NULL);
+
+        m_RegistryPath.Length = ArgRegistryPath->Length;
+        m_RegistryPath.MaximumLength = length;
+        m_RegistryPath.Buffer = (PWSTR) FxPoolAllocate(
+            GetDriverGlobals(), PagedPool, length);
+
+        if (m_RegistryPath.Buffer != NULL) {
+            RtlCopyMemory(m_RegistryPath.Buffer,
+                          ArgRegistryPath->Buffer,
+                          ArgRegistryPath->Length);
+
+            //
+            // other parts of WDF assumes m_RegistryPath.Buffer is
+            // a null terminated string.  make sure it is.
+            //
+            m_RegistryPath.Buffer[length/sizeof(WCHAR)- 1] = UNICODE_NULL;
+        }
+        else {
+            //
+            // We failed to allocate space for the registry path
+            // so set the length to 0.
+            //
+            m_RegistryPath.Length = 0;
+            m_RegistryPath.MaximumLength = 0;
+
+            status = STATUS_INSUFFICIENT_RESOURCES;
+        }
+    }
+
+    if (NT_SUCCESS(status)) {
+        if ((Config->DriverInitFlags & WdfDriverInitNoDispatchOverride) == 0) {
+            UCHAR i;
+
+            //
+            // Set up dispatch routines.
+            //
+            if (Config->DriverInitFlags & WdfDriverInitNonPnpDriver) {
+                //
+                // NT4 style drivers must clear the AddDevice field in the
+                // driver object so that they can be unloaded while still
+                // having device objects around.
+                //
+                // If AddDevice is set, NT considers the driver a pnp driver
+                // and will not allow net stop to unload the driver.
+                //
+                m_DriverObject.SetDriverExtensionAddDevice(NULL);
+
+                //
+                // Unload for an NT4 driver is still optional if the driver
+                // does not want to be stopped (through net stop for
+                // instance).
+                //
+                if (Config->EvtDriverUnload != NULL) {
+                    m_DriverObject.SetDriverUnload(Unload);
+                }
+                else {
+                    m_DriverObject.SetDriverUnload(NULL);
+                }
+
+            }
+            else {
+                //
+                // PnP driver, set our routines up
+                //
+                m_DriverObject.SetDriverExtensionAddDevice(AddDevice);
+                m_DriverObject.SetDriverUnload(Unload);
+            }
+
+            //
+            // For any major control code that we use a remove lock, the
+            // following locations must be updated:
+            //
+            // 1)  FxDevice::_RequiresRemLock() which decides if the remlock
+            //          is required
+            // 2)  FxDefaultIrpHandler::Dispatch might need to be changed if
+            //          there is catchall generic post processing that must be done
+            // 3)  Whereever the major code irp handler completes the irp or
+            //          sends it down the stack.  A good design would have all
+            //          spots in the irp handler where this is done to call a
+            //          common function.
+            //
+            WDFCASSERT(IRP_MN_REMOVE_DEVICE != 0x0);
+
+#if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
+            for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
+                if (FxDevice::_RequiresRemLock(i, 0x0) == FxDeviceRemLockNotRequired) {
+                    m_DriverObject.SetMajorFunction(i, FxDevice::Dispatch);
+                }
+                else {
+                    m_DriverObject.SetMajorFunction(i, FxDevice::DispatchWithLock);
+                }
+            }
+#else // USER_MODE
+            for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
+                if (FxDevice::_RequiresRemLock(i, 0x0) == FxDeviceRemLockNotRequired) {
+                    m_DriverObject.SetMajorFunction(i, FxDevice::DispatchUm);
+                }
+                else {
+                    m_DriverObject.SetMajorFunction(i, FxDevice::DispatchWithLockUm);
+                }
+            }
+#endif
+        }
+
+        //
+        // Determine if the debugger is connected.
+        //
+#if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
+        if (KD_DEBUGGER_ENABLED == TRUE && KD_DEBUGGER_NOT_PRESENT == FALSE) {
+            m_DebuggerConnected = TRUE;
+        }
+#endif
+
+        //
+        // Log this notable event after tracing has been initialized.
+        //
+
+
+
+
+
+
+        if ((Config->DriverInitFlags & WdfDriverInitNonPnpDriver) &&
+            Config->EvtDriverUnload == NULL) {
+
+            DoTraceLevelMessage(
+                FxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGDRIVER,
+                "Driver Object %p, reg path %wZ cannot be "
+                "unloaded, no DriverUnload routine specified",
+                m_DriverObject.GetObject(), &m_RegistryPath);
+        }
+
+#if FX_IS_USER_MODE
+        //
+        // Open a R/W handle to the driver's service parameters key
+        //
+        status = OpenParametersKey();
+        if (!NT_SUCCESS(status)) {
+            DoTraceLevelMessage(
+                FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
+                "Cannot open Driver Parameters key %!STATUS!",
+                status);
+        }
+#endif
+    }
+
+    return status;
+}
+
+_Must_inspect_result_
+FxString *
+FxDriver::GetRegistryPath(
+    VOID
+    )
+{
+    FxString *pString;
+
+    pString = new(GetDriverGlobals(), WDF_NO_OBJECT_ATTRIBUTES)
+        FxString(GetDriverGlobals());
+
+    if (pString != NULL) {
+        NTSTATUS status;
+
+        status = pString->Assign(m_RegistryPath.Buffer);
+
+        if (!NT_SUCCESS(status)) {
+            pString->Release();
+            pString = NULL;
+        }
+    }
+
+    return pString;
+}
+
+\f
+VOID
+FxDriver::ConfigureConstraints(
+    __in_opt PWDF_OBJECT_ATTRIBUTES DriverAttributes
+    )
+/*++
+
+Routine Description:
+
+    Determine the pointer to the proper lock to acquire
+    for event callbacks from the FxDriver to the device driver
+    depending on the configured locking model.
+
+Arguments:
+    DriverAttributes - caller supplied scope and level, used only if they
+        are not InheritFromParent.
+
+Returns:
+    None
+
+--*/
+{
+    BOOLEAN automaticLockingRequired;
+
+    automaticLockingRequired = FALSE;
+
+    // Initialize the mutex lock
+    m_CallbackMutexLock.Initialize(this);
+
+
+
+
+
+
+
+
+
+    MarkPassiveCallbacks(ObjectDoNotLock);
+
+    m_CallbackLockPtr = &m_CallbackMutexLock;
+    m_CallbackLockObjectPtr = this;
+
+    //
+    // Use the caller supplied scope and level only if they are not
+    // InheritFromParent.
+    //
+    if (DriverAttributes != NULL) {
+
+        if (DriverAttributes->ExecutionLevel !=
+                WdfExecutionLevelInheritFromParent) {
+            m_ExecutionLevel = DriverAttributes->ExecutionLevel;
+        }
+
+        if (DriverAttributes->SynchronizationScope !=
+                WdfSynchronizationScopeInheritFromParent) {
+            m_SynchronizationScope = DriverAttributes->SynchronizationScope;
+        }
+    }
+
+    //
+    // If the driver asks for any synchronization, we synchronize the
+    // WDFDRIVER object's own callbacks as well.
+    //
+    // (No option to extend synchronization for regular operations
+    //  across all WDFDEVICE objects)
+    //
+    if (m_SynchronizationScope == WdfSynchronizationScopeDevice ||
+        m_SynchronizationScope == WdfSynchronizationScopeQueue) {
+
+        automaticLockingRequired = TRUE;
+    }
+
+    //
+    // No FxDriver events are delivered from a thread that is
+    // not already at PASSIVE_LEVEL, so we don't need to
+    // allocate an FxSystemWorkItem if the execution level
+    // constraint is WdfExecutionLevelPassive.
+    //
+    // If any events are added FxDriver that could occur on a thread
+    // that is above PASSIVE_LEVEL, then an FxSystemWorkItem would
+    // need to be allocated to deliver those events similar to the
+    // code in FxIoQueue.
+    //
+
+    //
+    // Configure FxDriver event callback locks
+    //
+    if (automaticLockingRequired) {
+        m_DriverDeviceAdd.SetCallbackLockPtr(m_CallbackLockPtr);
+    }
+    else {
+        m_DriverDeviceAdd.SetCallbackLockPtr(NULL);
+    }
+}
+