--- /dev/null
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ FxObject.cpp
+
+Abstract:
+
+ This module contains the implementation of the base object
+
+Author:
+
+
+
+
+
+Environment:
+
+ Both kernel and user mode
+
+Revision History:
+
+
+
+
+
+
+
+
+
+
+--*/
+
+#include "fxobjectpch.hpp"
+
+extern "C" {
+
+#if defined(EVENT_TRACING)
+#include "FxObject.tmh"
+#else
+ULONG DebugLevel = TRACE_LEVEL_INFORMATION;
+ULONG DebugFlag = 0xff;
+#endif
+
+}
+
+FxObject::FxObject(
+ __in WDFTYPE Type,
+ __in USHORT Size,
+ __in PFX_DRIVER_GLOBALS FxDriverGlobals
+ ) :
+ m_Type(Type),
+ m_ObjectSize((USHORT) WDF_ALIGN_SIZE_UP(Size, MEMORY_ALLOCATION_ALIGNMENT)),
+ m_Globals(FxDriverGlobals)
+#if FX_CORE_MODE==FX_CORE_USER_MODE
+#ifndef INLINE_WRAPPER_ALLOCATION
+ ,m_COMWrapper(NULL)
+#endif
+#endif
+{
+ ASSERT((((ULONG_PTR) this) & FxHandleFlagMask) == 0x0);
+
+ Construct(FALSE);
+}
+
+FxObject::FxObject(
+ __in WDFTYPE Type,
+ __in USHORT Size,
+ __in PFX_DRIVER_GLOBALS FxDriverGlobals,
+ __in FxObjectType ObjectType
+ ) :
+ m_Type(Type),
+ m_ObjectSize((USHORT) WDF_ALIGN_SIZE_UP(Size, MEMORY_ALLOCATION_ALIGNMENT)),
+ m_Globals(FxDriverGlobals)
+{
+ if (ObjectType != FxObjectTypeEmbedded) {
+ //
+ // only for non embedded objects
+ //
+ ASSERT((((ULONG_PTR) this) & FxHandleFlagMask) == 0x0);
+ }
+
+ Construct(ObjectType == FxObjectTypeEmbedded ? TRUE : FALSE);
+}
+
+FxObject::~FxObject()
+{
+ FxTagTracker *pTagTracker;
+
+
+
+
+
+
+
+ pTagTracker = GetTagTracker();
+
+ if (pTagTracker != NULL) {
+ delete pTagTracker;
+ }
+
+ ASSERT(m_DisposeSingleEntry.Next == NULL);
+
+ //
+ // We need to ensure there are no leaked child objects, or
+ // parent associations.
+ //
+ // This can occur if someone calls the C++ operator delete,
+ // or Release() to destroy the object.
+ //
+ // This is generally invalid in the framework, though certain
+ // objects may understand the underlying contract, but must
+ // make sure there are no left over un-Disposed associations.
+ //
+ // Embedded FxObject's also don't have delete called on them,
+ // and have to manually manage any child associations when
+ // their parent object disposes by calling PerformEarlyDispose.
+ //
+ // They don't have an associated lifetime parent since they
+ // are embedded.
+ //
+ if (m_ParentObject != NULL ||
+ !IsListEmpty(&m_ChildListHead) || !IsListEmpty(&m_ChildEntry)) {
+ PCSTR pHandleName;
+
+ pHandleName = FxObjectTypeToHandleName(m_Type);
+ if (pHandleName == NULL) {
+ pHandleName = "WDFOBJECT";
+ }
+
+ ASSERTMSG(
+ "Object was freed using WdfObjectDereference, not WdfObjectDelete\n",
+ m_ParentObject == NULL &&
+ IsListEmpty(&m_ChildEntry) &&
+ IsListEmpty(&m_ChildListHead)
+ );
+
+ DoTraceLevelMessage(
+ GetDriverGlobals(), TRACE_LEVEL_FATAL, TRACINGOBJECT,
+ "Handle %s %p (raw object %p) was freed using "
+ "WdfObjectDereference(), not WdfObjectDelete()",
+ pHandleName, GetObjectHandleUnchecked(), this);
+
+ FxVerifierBugCheck(GetDriverGlobals(),
+ WDF_OBJECT_ERROR,
+ (ULONG_PTR) GetObjectHandleUnchecked(),
+ (ULONG_PTR) this);
+ }
+
+ //
+ // This is called when the reference count goes to zero
+ //
+ SetObjectStateLocked(FxObjectStateDestroyed);
+}
+
+
+VOID
+FX_VF_METHOD(FxObject, VerifyConstruct) (
+ _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
+ _In_ BOOLEAN Embedded
+ )
+{
+ UNREFERENCED_PARAMETER(FxDriverGlobals);
+
+#if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
+ PAGED_CODE_LOCKED();
+#endif
+
+ ASSERTMSG(
+ "this object's type is not listed in FxObjectsInfo\n",
+ FxVerifyObjectTypeInTable(m_Type));
+
+ //
+ // If this is an embedded object, there is no possibility of having
+ // a debug extension, so do not set FXOBJECT_FLAGS_HAS_DEBUG *ever*
+ // in this case.
+ //
+ if (m_Globals->IsObjectDebugOn() && Embedded == FALSE) {
+ FxObjectDebugExtension* pExtension;
+
+ m_ObjectFlags |= FXOBJECT_FLAGS_HAS_DEBUG;
+
+ pExtension = GetDebugExtension();
+ ASSERT(pExtension->Signature == FxObjectDebugExtensionSignature);
+
+ //
+ // Assume that zero is an invalid state.
+ //
+ WDFCASSERT(FxObjectStateInvalid == 0x0);
+ RtlZeroMemory(&pExtension->StateHistory[0],
+ ARRAY_SIZE(pExtension->StateHistory));
+ pExtension->StateHistoryIndex = 0;
+
+ //
+ // Setup the first slot to our new state.
+ //
+ pExtension->StateHistory[0] = FxObjectStateCreated;
+
+ AllocateTagTracker(m_Type);
+ }
+}
+
+
+VOID
+FxObject::FinalRelease(
+ VOID
+ )
+{
+
+
+
+
+
+
+
+
+
+
+
+
+ //
+ // No other access, OK to test flag without grabbing spinlock
+ // since it can only be set at create.
+ //
+ if (ShouldDeferDisposeLocked()) {
+ //
+ // If this is a passive level only object, ensure we only destroy
+ // it from passive level. No need to hold the object lock when
+ // changing to this state since no other thread can change our
+ // state.
+ //
+ SetObjectStateLocked(FxObjectStateDeferedDestroy);
+
+ //
+ // Note, we cannot be holding a lock while making this call b/c by
+ // the time it returns, it could have freed the object and the
+ // KeReleaseSpinLock call would have touched freed pool.
+ //
+
+ //FxToObjectItf::FxAddToDriverDisposeList(m_Globals, this);
+
+
+ m_Globals->Driver->GetDisposeList()->Add(this);
+
+ }
+ else {
+ ProcessDestroy();
+ }
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxObject::QueryInterface(
+ __in FxQueryInterfaceParams* Params
+ )
+{
+ NTSTATUS status;
+
+ if (Params->Type == FX_TYPE_OBJECT) {
+ *Params->Object = this;
+ status = STATUS_SUCCESS;
+ }
+ else {
+ status = STATUS_NOINTERFACE;
+ }
+
+ return status;
+}
+
+VOID
+FxObject::AllocateTagTracker(
+ __in WDFTYPE Type
+ )
+{
+ ASSERT(IsDebug());
+
+ if (m_Globals->DebugExtension != NULL &&
+ m_Globals->DebugExtension->ObjectDebugInfo != NULL &&
+ FxVerifierGetTrackReferences(
+ m_Globals->DebugExtension->ObjectDebugInfo,
+ Type)) {
+ //
+ // Failure to CreateAndInitialize a tag tracker is no big deal, we just
+ // don't track references.
+ //
+
+ (void) FxTagTracker::CreateAndInitialize(
+ &GetDebugExtension()->TagTracker,
+ m_Globals,
+ FxTagTrackerTypeHandle,
+ FALSE,
+ this
+ );
+
+ //
+ // For now we overload the requirement of a tag tracker as also tracing
+ // state changes.
+ //
+ m_ObjectFlags |= FXOBJECT_FLAGS_TRACE_STATE;
+ }
+}
+
+VOID
+FxObject::operator delete(
+ __in PVOID Memory
+ )
+{
+ ASSERT(Memory != NULL);
+
+
+
+
+
+
+
+
+
+
+
+
+
+ FxPoolFree(_GetBase((FxObject*) Memory));
+}
+
+VOID
+FxObject::CallCleanupCallbacks(
+ VOID
+ )
+{
+ FxContextHeader* pHeader;
+ WDFOBJECT h;
+
+ //
+ // Deleted before Commit or it is an internal object
+ //
+ if (IsCommitted() == FALSE) {
+ return;
+ }
+
+ //
+ // We only should have an object handle when we have an external object
+ //
+ h = GetObjectHandle();
+
+ for (pHeader = GetContextHeader();
+ pHeader != NULL;
+ pHeader = pHeader->NextHeader) {
+ if (pHeader->EvtCleanupCallback != NULL) {
+ pHeader->EvtCleanupCallback(h);
+ pHeader->EvtCleanupCallback = NULL;
+ }
+ }
+
+ m_ObjectFlags &= ~FXOBJECT_FLAGS_HAS_CLEANUP;
+}
+
+VOID
+FxObject::ClearEvtCallbacks(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Clears out any assigned callbacks on the object.
+
+Arguments:
+ None
+
+Return Value:
+ None
+
+ --*/
+{
+ FxContextHeader *pHeader;
+
+ for (pHeader = GetContextHeader();
+ pHeader != NULL;
+ pHeader = pHeader->NextHeader) {
+
+ pHeader->EvtDestroyCallback = NULL;
+ pHeader->EvtCleanupCallback = NULL;
+ }
+
+ m_ObjectFlags &= ~FXOBJECT_FLAGS_HAS_CLEANUP;
+}
+
+VOID
+FxObject::DeleteFromFailedCreate(
+ VOID
+ )
+/*++
+
+Routine Description:
+ Clears out any assigned callbacks on the object and then deletes it. Clearing
+ out the callbacks are necessary so that the driver's callbacks are not called
+ on a buffer that they didn't initialize.
+
+Arguments:
+ None
+
+Return Value:
+ None
+
+ --*/
+{
+ ClearEvtCallbacks();
+
+ //
+ // After this call returns "this" is destroyed is not a valid pointer!
+ //
+ DeleteObject();
+
+ //
+ // "this" is now freed memory, do not touch it!
+ //
+}
+
+//
+// FxObject Parent/Child Rules:
+//
+// The FxObject state machine protects state transitions when
+// objects are being associated with each other, and races
+// that can occur when a child and parent object are being
+// deleted at the same time. This provides the backbone of
+// assurance on the objects state.
+//
+// While transiting object states, the following must be taken
+// into consideration:
+//
+// Reference counts:
+//
+// When an object is created with operator new(), it has a reference count
+// of one.
+//
+// When a DeleteObject is done on it, the reference count is decremented
+// after delete processing is done (which can include disposing children),
+// and this results in the objects destruction.
+//
+// When an object is created by operator new() and immediately associated
+// with a parent object, its reference count remains one.
+//
+// When either the DeleteObject() method is invoked, or the parent object
+// disposes the child object with ParentDeleteEvent(), eventually the
+// object will dereference itself after delete processing is done only
+// once.
+//
+// Even in the face of race conditions, the object state machine ensures that
+// only one set of the proper delete conditions occur (dispose children, invoke
+// driver and class dispose callbacks, dereference object).
+//
+// An object is responsible for releasing its own reference count on its
+// self when it goes into the FxObjectStateDeletedAndDisposed.
+//
+// This has been *carefully* designed so that only the original object
+// references are required for this automatic lifetime case to avoid
+// extra interlocked operations in AddRef and Release frequently. These
+// extra interlocked operations can have a big performance impact on
+// the WDF main I/O paths, in which object relationship's are being used.
+//
+// A simpler implementation may try and have the parent add a reference
+// to the child, and the child add a reference to the parent, but this
+// is a tightly coupled implementation controlled by the object state
+// machine. A circular reference pattern is OK for objects that
+// do not have a formal designed in relationship that can be expressed
+// in the complex tear down contract implemented in the state machine.
+//
+// SpinLocks:
+//
+// The m_SpinLock field protects each indivdual objects m_ObjectState
+// variable, the list of child objects that it has, and the m_ParentObject
+// field.
+//
+// In addition, if any object associates itself with a parent object,
+// it effectively "lends" its m_ChildEntry field to the parent object,
+// and these are manipulated and protected by the parent objects m_SpinLock.
+//
+// Lock Order:
+//
+// Currently the lock order is Child -> Parent, meaning a child
+// can call the AddChildObjectInternal, RemoveChildObjectInternal, methods with
+// the child lock held.
+//
+// The parent object will not invoke any child object ParentDeleteEvent()
+// while holding the parents m_SpinLock.
+//
+// This order allows potential races between child DeleteObject and parent
+// Dispose to be resolved without extra reference counts and multiple
+// acquires and releases of the spinlocks in the normal cases.
+//
+//
+// AddChildObjectInternal/RemoveChildObjectInternal:
+//
+// When a child object is added to the parent, the parent can delete
+// the child object at any time when the parent object is deleted
+// or disposed.
+//
+// If a call to RemoveChildObjectInternal is made, the caller may not "win"
+// the inherent race with a parent object Dispose() occuring. This
+// is similar to the WDM IRP cancel race, and is handled in exactly
+// the same fashion.
+//
+// If an object is associated with a parent, and later removal
+// is desired, the return status of RemoveChildObjectInternal must be
+// tested, and if not success, the caller should not delete the
+// child object itself, since the parent is in the process
+// of doing so. The caller "lost the Dispose race".
+//
+//
+// m_ParentObject field:
+//
+// This field is set by the child object after it successfully
+// adds a parent object, and clear it when it is disposed by
+// the parent, or removes the parent association.
+//
+// It is protected by the childs m_SpinLock field.
+//
+
+
+
+
+
+
+
+_Must_inspect_result_
+NTSTATUS
+FxObject::AssignParentObject(
+ __in FxObject* ParentObject
+ )
+/*++
+
+Routine Description:
+ Assign a parent to the current object. The parent can not be the same
+ object.
+
+Arguments:
+ ParentObject - Object to become the parent for this object
+
+Returns:
+
+Comments:
+
+ The caller "passes" its initial object reference to us, so we
+ do not take an additional reference on the object.
+
+ If we are deleted, Dispose() will be invoked on the object, and
+ its reference will be released.
+
+ This provides automatic deletion of associated child objects if
+ the object does not keep any extra references.
+
+--*/
+{
+ KIRQL oldIrql;
+ NTSTATUS status;
+
+ m_SpinLock.Acquire(&oldIrql);
+
+ //
+ // Can't add a parent if the current object is being deleted
+ //
+ if (m_ObjectState != FxObjectStateCreated) {
+ TraceDroppedEvent(FxObjectDroppedEventAssignParentObject);
+ m_SpinLock.Release(oldIrql);
+ return STATUS_DELETE_PENDING;
+ }
+
+ //
+ // Current Object can't already have a parent, and can't
+ // be its own parent.
+ //
+ if (m_ParentObject != NULL) {
+ m_SpinLock.Release(oldIrql);
+ return STATUS_WDF_PARENT_ALREADY_ASSIGNED;
+ }
+
+ if (m_ParentObject == this) {
+ m_SpinLock.Release(oldIrql);
+ return STATUS_WDF_PARENT_IS_SELF;
+ }
+
+ //
+ // We don't allow a parent object to be assigned after
+ // FxObject::Commit().
+ //
+ ASSERTMSG("Parent object can not be assigned after Commit()\n", !IsCommitted());
+
+ ASSERT(IsListEmpty(&this->m_ChildEntry));
+
+ status = ParentObject->AddChildObjectInternal(this);
+
+ if (NT_SUCCESS(status)) {
+ m_ParentObject = ParentObject;
+ }
+
+ m_SpinLock.Release(oldIrql);
+
+ return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxObject::AddContext(
+ __in FxContextHeader *Header,
+ __in PVOID* Context,
+ __in PWDF_OBJECT_ATTRIBUTES Attributes
+ )
+{
+ FxContextHeader *pCur, **ppLast;
+ NTSTATUS status;
+ KIRQL irql;
+
+ status = STATUS_UNSUCCESSFUL;
+
+ pCur = GetContextHeader();
+
+ //
+ // This should never happen since all outward facing objects have a
+ // context header; framework never calls this function on internal
+ // objects.
+ //
+ ASSERT(pCur != NULL);
+
+ //
+ // Acquire the lock to lock the object's state. A side affect of grabbing
+ // the lock is that all updaters who want to add a context are serialized.
+ // All callers who want to find a context do not need to acquire the lock
+ // becuase they are not going to update the list, just read from it.
+ //
+ // Once a context has been added, it will not be removed until the object
+ // has been deleted.
+ //
+ m_SpinLock.Acquire(&irql);
+
+ if (m_ObjectState == FxObjectStateCreated && pCur != NULL) {
+ //
+ // Iterate over the list of contexts already on this object and see if
+ // this type already is attached.
+ //
+ for (ppLast = &pCur->NextHeader;
+ pCur != NULL;
+ ppLast = &pCur->NextHeader, pCur = pCur->NextHeader) {
+
+ if (pCur->ContextTypeInfo == Header->ContextTypeInfo) {
+ //
+ // Dupe found, return error but give the caller the context
+ // pointer
+ //
+ if (Context != NULL) {
+ *Context = &pCur->Context[0];
+ }
+
+ status = STATUS_OBJECT_NAME_EXISTS;
+ break;
+ }
+ }
+
+ if (pCur == NULL) {
+ //
+ // By using the interlocked to update, we don't need to use a lock
+ // when walking the list to find the context. The only reason
+ // we are holding the object lock is to lock the current state
+ // (m_ObjectState) of the object.
+ //
+ InterlockedExchangePointer((PVOID*) ppLast, Header);
+ status = STATUS_SUCCESS;
+
+ if (Context != NULL) {
+ *Context = &Header->Context[0];
+ }
+
+ //
+ // FxContextHeaderInit does not set these callbacks. If this were
+ // the creation of the object itself, FxObject::Commit would have done
+ // this assignment.
+ //
+ Header->EvtDestroyCallback = Attributes->EvtDestroyCallback;
+
+ if (Attributes->EvtCleanupCallback != NULL) {
+ Header->EvtCleanupCallback = Attributes->EvtCleanupCallback;
+ m_ObjectFlags |= FXOBJECT_FLAGS_HAS_CLEANUP;
+ }
+
+ }
+ }
+ else {
+ //
+ // Object is being torn down, adding a context is a bad idea because we
+ // cannot guarantee that the cleanup or destroy routines will be called
+ //
+ status = STATUS_DELETE_PENDING;
+ }
+
+ m_SpinLock.Release(irql);
+
+ return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxObject::AddChildObjectInternal(
+ __in FxObject* ChildObject
+ )
+
+/*++
+
+Routine Description:
+ Called by an object to be added to this objects child list
+
+Arguments:
+ ChildObject - Object to add this this objects child list
+
+Returns:
+ NTSTATUS
+
+Comments:
+ The caller "passes" its initial object reference to us, so we
+ do not take an additional reference on the object.
+
+ If we are deleted, Dispose() will be invoked on the object, and
+ its reference will be released.
+
+ This provides automatic deletion of associated child objects if
+ the object does not keep any extra references.
+
+--*/
+{
+ KIRQL oldIrql;
+
+ m_SpinLock.Acquire(&oldIrql);
+
+ //
+ // Can't add child if the current object is being deleted
+ //
+ if (m_ObjectState != FxObjectStateCreated) {
+ TraceDroppedEvent(FxObjectDroppedEventAddChildObjectInternal);
+ m_SpinLock.Release(oldIrql);
+ return STATUS_DELETE_PENDING;
+ }
+
+ //
+ // ChildObject can't already have a parent, and can't
+ // be its own parent.
+ //
+ ASSERT(ChildObject->m_ParentObject == NULL);
+ ASSERT(IsListEmpty(&ChildObject->m_ChildEntry));
+ ASSERT(ChildObject != this);
+
+ //
+ // Add to our m_ChildList
+ //
+ InsertTailList(&m_ChildListHead, &ChildObject->m_ChildEntry);
+
+ if (ChildObject->GetDeviceBase() == NULL) {
+ //
+ // Propagate the device base downward to the child
+ //
+ ChildObject->SetDeviceBase(GetDeviceBase());
+ }
+
+ m_SpinLock.Release(oldIrql);
+
+ return STATUS_SUCCESS;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxObject::RemoveChildObjectInternal(
+ __in FxObject* ChildObject
+ )
+/*++
+
+Routine Description:
+
+ Remove a ChildObject from our child associations list.
+
+ The ChildObject must exist in our list if we are not
+ otherwise disposing or deleting ourselves.
+
+ If we are not disposing, the child is removed from the list
+ and its reference count is unmodified.
+
+ If we are disposing, a failure is returned so that the caller
+ does not delete or dereference the child object itself, since
+ this is similar to a cancel IRP race condition.
+
+Arguments:
+ ChildObject - the object to remove this object's list of children
+
+Returns:
+
+ STATUS_SUCCESS - Child was removed from the list, no parent Dispose()
+ can occur.
+
+ !STATUS_SUCCESS - Child can not be removed from the list, and is being
+ Disposed by the parent. The caller must *not* delete
+ the object itself.
+
+--*/
+
+{
+ KIRQL oldIrql;
+
+ m_SpinLock.Acquire(&oldIrql);
+
+ //
+ // Object is already being deleted, this object will be removed as a child
+ // by the parents Dispose()
+ //
+ if (m_ObjectState != FxObjectStateCreated) {
+ TraceDroppedEvent(FxObjectDroppedEventRemoveChildObjectInternal);
+ m_SpinLock.Release(oldIrql);
+ return STATUS_DELETE_PENDING;
+ }
+
+ //
+ // We should be the child object's parent
+ //
+ ASSERT(ChildObject->m_ParentObject == this);
+
+ //
+ // Child should be on our list
+ //
+ ASSERT(!IsListEmpty(&ChildObject->m_ChildEntry));
+
+ //
+ // We should have entries if someone wants to remove from our list
+ //
+ ASSERT(!IsListEmpty(&m_ChildListHead));
+
+ RemoveEntryList(&ChildObject->m_ChildEntry);
+
+ //
+ // Mark it removed
+ //
+ InitializeListHead(&ChildObject->m_ChildEntry);
+
+ //
+ // We did not take a reference on the child object when it
+ // was added to the list, so we do not dereference it
+ // on the remove call.
+ //
+ // Note: We only dereference child objects when we are deleted
+ // ourselves, not when the child object manually breaks the
+ // association by calling this method.
+ //
+ m_SpinLock.Release(oldIrql);
+
+ return STATUS_SUCCESS;
+}
+
+_Must_inspect_result_
+FxObject*
+FxObject::GetParentObjectReferenced(
+ __in PVOID Tag
+ )
+
+/*++
+
+Routine Description:
+ Return this objects parent, which could be NULL if
+ the object is not part of an association, or this or
+ the parent object is deleting.
+
+ An extra reference is taken on the parent object which
+ must eventually be released by the caller.
+
+Arguments:
+ Tag - Tag to use when referencing the parent
+
+Returns:
+
+ Parent object, otherwise NULL if no parent for this object
+
+--*/
+
+{
+ KIRQL oldIrql;
+ FxObject* parentObject;
+
+ m_SpinLock.Acquire(&oldIrql);
+
+ if (m_ObjectState == FxObjectStateCreated) {
+ parentObject = m_ParentObject;
+ }
+ else {
+ // Parent is disposing us, or we are being disposed
+ parentObject = NULL;
+ }
+
+ if (parentObject != NULL) {
+ parentObject->ADDREF(Tag);
+ }
+
+ m_SpinLock.Release(oldIrql);
+
+ return parentObject;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxObject::Commit(
+ __in_opt PWDF_OBJECT_ATTRIBUTES Attributes,
+ __out_opt WDFOBJECT* ObjectHandle,
+ __in_opt FxObject* Parent,
+ __in BOOLEAN AssignDriverAsDefaultParent
+ )
+/*++
+
+Routine Description:
+ Commit the object before returning the handle to the caller.
+
+Arguments:
+ Attributes - PWDF_OBJECT_ATTRIBUTES to assign to this object
+
+ ObjectHandle - Location to return the objects handle
+
+Returns:
+ NTSTATUS of the result. STATUS_SUCCESS if success.
+
+ Returns WDFOBJECT handle if success.
+
+--*/
+{
+ NTSTATUS status;
+ WDFOBJECT object;
+ FxObject* parent;
+
+ parent = NULL;
+
+ if (m_ObjectSize == 0) {
+ ASSERTMSG("Only external objects can call Commit()\n",
+ m_ObjectSize != 0);
+ return STATUS_INVALID_HANDLE;
+ }
+
+ //
+ // For an object to be committed into a handle, it needs to have an object
+ // size. Internal objects set their size to zero as the indication they
+ // are internal and will not be converted into handles.
+ //
+ ASSERT(m_ObjectSize != 0);
+
+ //
+ // Caller has already validated basic WDF_OBJECT_ATTRIBUTES
+ // with FxValidateObjectAttributes
+ //
+
+ //
+ // Set object execution level constraint if specified
+ //
+ if (Attributes != NULL &&
+ Attributes->ExecutionLevel == WdfExecutionLevelPassive) {
+ MarkPassiveCallbacks();
+ }
+
+ //
+ // Assign parent if supplied
+ //
+ if (Parent != NULL) {
+ parent = Parent;
+ }
+ else if (Attributes != NULL && Attributes->ParentObject != NULL) {
+ FxObjectHandleGetPtr(
+ m_Globals,
+ Attributes->ParentObject,
+ FX_TYPE_OBJECT,
+ (PVOID*)&parent
+ );
+ }
+ else {
+
+ //
+ // If the object already does not have a parent, and
+ // one has not been specified we default it to FxDriver.
+ //
+ // We check to ensure we are not FxDriver being created.
+ //
+ if (AssignDriverAsDefaultParent &&
+ m_ParentObject == NULL) {
+
+ //parent = FxToObjectItf::FxGetDriverAsDefaultParent(m_Globals, this);
+
+
+ if (m_Globals->Driver != this) {
+ parent = m_Globals->Driver;
+ }
+
+ }
+ }
+
+ ASSERT(parent != this);
+
+ if (parent != NULL) {
+ //
+ // Make it the parent of this object
+ //
+ status = AssignParentObject(parent);
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+ }
+
+ //
+ // Now assign the optional EvtObjectCleanup, EvtObjectDestroy callbacks
+ //
+ if (Attributes != NULL) {
+ FxContextHeader* pHeader;
+
+ pHeader = GetContextHeader();
+
+ if (Attributes->EvtDestroyCallback != NULL) {
+ pHeader->EvtDestroyCallback = Attributes->EvtDestroyCallback;
+ }
+
+ if (Attributes->EvtCleanupCallback != NULL) {
+ pHeader->EvtCleanupCallback = Attributes->EvtCleanupCallback;
+ m_ObjectFlags |= FXOBJECT_FLAGS_HAS_CLEANUP;
+ }
+ }
+
+ //
+ // We mark the handle as committed so that we can create the handle.
+ //
+ MarkCommitted();
+
+ //
+ // Create the object handle, assign EvtObjectCleanup, EvtObjectDestroy
+ //
+ FxObjectHandleCreate(this, &object);
+
+ if (ObjectHandle != NULL) {
+ *ObjectHandle = object;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxObject::_GetEffectiveLock(
+ __in FxObject* Object,
+ __in_opt IFxHasCallbacks* Callbacks,
+ __in BOOLEAN AutomaticLocking,
+ __in BOOLEAN PassiveCallbacks,
+ __out FxCallbackLock** CallbackLock,
+ __out_opt FxObject** CallbackLockObject
+ )
+/*++
+
+Routine Description:
+
+ This gets the effective lock based on the callback constraints
+ configuration of the supplied object.
+
+ This is a common routine shared by all FxObject's that utilize
+ DPC's. Currently, this is FxDpc, FxTimer, and FxInterrupt.
+
+ This contains the common serialization configuration logic for these
+ objects.
+
+Arguments:
+
+ Object - Object to serialize with
+
+ Callbacks - Optional interface for acquiring constraints and locking pointers
+
+ AutomaticLocking - TRUE if automatic serialization with Object is required
+
+ PassiveCallbacks - TRUE if the caller requires passive level callback, FALSE
+ if the
+ CallbackLock - Lock that is in effect for Object
+
+ CallbackLockOjbect - FxObject that contains the callback lock
+
+Returns:
+
+ NTSTATUS
+
+--*/
+{
+ PFX_DRIVER_GLOBALS pFxDriverGlobals;
+ WDF_EXECUTION_LEVEL parentLevel;
+ WDF_SYNCHRONIZATION_SCOPE parentScope;
+
+ pFxDriverGlobals = Object->GetDriverGlobals();
+ *CallbackLock = NULL;
+ *CallbackLockObject = NULL;
+
+ //
+ // No automatic locking, nothing to do
+ //
+ if (AutomaticLocking == FALSE) {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Objects that have callback locks must support this interface.
+ //
+ if (Callbacks == NULL) {
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ //
+ // Get the callback constraints in effect for the object
+ //
+ Callbacks->GetConstraints(&parentLevel, &parentScope);
+
+ if (parentScope == WdfSynchronizationScopeInheritFromParent ||
+ parentScope == WdfSynchronizationScopeNone) {
+ //
+ // Do nothing, no synchronization specified
+ //
+ DO_NOTHING();
+ }
+ else {
+ //
+ // If the caller wants passive callbacks and the object does not support
+ // it, failure.
+ //
+ // If the caller wants non passive callbacks and the object supports
+ // passive only callbacks, failure.
+ //
+ if ((PassiveCallbacks && Object->IsPassiveCallbacks() == FALSE) ||
+ (PassiveCallbacks == FALSE && Object->IsPassiveCallbacks())) {
+ FxVerifierDbgBreakPoint(pFxDriverGlobals);
+ return STATUS_WDF_INCOMPATIBLE_EXECUTION_LEVEL;
+ }
+
+ *CallbackLock = Callbacks->GetCallbackLockPtr(CallbackLockObject);
+ }
+
+ return STATUS_SUCCESS;
+}