VOID
FxIoTargetRemote::Close(
    __in FxIoTargetRemoteCloseReason Reason
    )
{
    FxIoTargetClearedPointers pointers;
    MdTargetNotifyHandle pNotifyHandle;
    SINGLE_LIST_ENTRY sent;
    LIST_ENTRY pended;
    WDF_IO_TARGET_STATE removeState;
    KIRQL irql;
    BOOLEAN wait;
    PFX_DRIVER_GLOBALS pFxDriverGlobals;

    pFxDriverGlobals = GetDriverGlobals();
    DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
        "enter:  WDFIOTARGET %p, reason %d", GetObjectHandle(), Reason);

    RtlZeroMemory(&pointers, sizeof(pointers));
    pNotifyHandle = NULL;

    sent.Next = NULL;
    InitializeListHead(&pended);

    wait = FALSE;

    //
    // Pick a value that is not used anywhere in the function and make sure that 
    // we have changed it, before we go to the Remove state
    //
#pragma prefast(suppress: __WARNING_UNUSED_SCALAR_ASSIGNMENT, "PFD is warning that the following assignement is unused. Suppress it to prevent changing any logic.")
    removeState = WdfIoTargetStarted;

CheckState:
    Lock(&irql);

    //
    // If we are in the process of opening the target, wait for that to finish.
    //
    if (m_OpenState == FxIoTargetRemoteOpenStateOpening) {
        Unlock(irql);

        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
            "Closing WDFIOTARGET %p which is opening, waiting on event %p",
            GetObjectHandle(), m_OpenedEvent.GetEvent());

        m_OpenedEvent.EnterCRAndWaitAndLeave();

        //
        // Jump back to the top and recheck
        //
        goto CheckState;
    }

    if (Reason == FxIoTargetRemoteCloseReasonDelete) {
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
            "Closing WDFIOTARGET %p, reason:   delete", GetObjectHandle());

        removeState = WdfIoTargetDeleted;
    }
    else if (m_OpenState == FxIoTargetRemoteOpenStateOpen) {
        if (Reason == FxIoTargetRemoteCloseReasonQueryRemove) {
            DoTraceLevelMessage(
                pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
                "Closing WDFIOTARGET %p, reason:   query remove",
                GetObjectHandle());
            //
            // Not really being removed, but that is what the API name is...
            //
            removeState = WdfIoTargetClosedForQueryRemove;
        }
        else {
                       
            DoTraceLevelMessage(
                pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
                "Closing WDFIOTARGET %p, reason:   close", GetObjectHandle());
            
            if (pFxDriverGlobals->IsVersionGreaterThanOrEqualTo(1,9)) {
                removeState = WdfIoTargetClosed;
            }
            else {
                removeState = WdfIoTargetClosedForQueryRemove;
            }            
        }
               
        //
        // Either way, we are no longer open for business
        //
        m_OpenState = FxIoTargetRemoteOpenStateClosed;
    }
    else {
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
            "Closing WDFIOTARGET %p which is not open", GetObjectHandle());

        //
        // We are not opened, so treat this as a cleanup
        //
        removeState = WdfIoTargetClosed;
    }

    DoTraceLevelMessage(
        pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
        "WDFIOTARGET %p:  fileobj %p, devobj %p, handle %p, notify handle %I64d",
        GetObjectHandle(), m_TargetFileObject,
        m_TargetDevice, m_TargetHandle, (UINT64)m_TargetNotifyHandle);

    if (Reason != FxIoTargetRemoteCloseReasonQueryRemove) {
        //
        // If we are closing for a query remove, we want to keep the handle
        // around so that we can be notified of the final close or if the close
        // was canceled.
        //
        pNotifyHandle = m_TargetNotifyHandle;
        ResetTargetNotifyHandle();
    }

    ASSERT(removeState != WdfIoTargetStarted);
    m_ClearedPointers =  &pointers;
    GotoRemoveState(removeState, &pended, &sent, FALSE, &wait);

    Unlock(irql);

    UnregisterForPnpNotification(pNotifyHandle);

    //
    // Complete any requests we might have pulled off of our lists
    //
    CompletePendedRequestList(&pended);
    _CancelSentRequests(&sent);

    //
    // We were just removed, wait for any I/O to complete back if necessary.
    //
    if (wait) {
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
            "WDFIOTARGET %p, waiting for stop to complete", GetObjectHandle());

        WaitForSentIoToComplete();
    }

    switch (Reason) {
    case FxIoTargetRemoteCloseReasonQueryRemove:
        //
        // m_OpenParams is needed for reopen on canceled query remove
        //
        DO_NOTHING();
        break;

    case FxIoTargetRemoteCloseReasonDelete:
        m_OpenParams.Clear();
        break;

    default:
        //
        // If this object is not about to be deleted, we need to revert some
        // of the state that just changed.
        //
        m_SentIoEvent.Clear();
        break;
    }
    
    if (removeState == WdfIoTargetDeleted) {
       WaitForDisposeEvent();
    }

    //
    // Finally, close down our handle and pointers
    //
    if (pointers.TargetPdo != NULL) {
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
            "WDFIOTARGET %p derefing PDO %p on close",
            GetObjectHandle(), pointers.TargetPdo);

        Mx::MxDereferenceObject(pointers.TargetPdo);
    }

    if (pointers.TargetFileObject != NULL) {
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
            "WDFIOTARGET %p derefing FileObj %p on close",
            GetObjectHandle(), pointers.TargetFileObject);
        Mx::MxDereferenceObject(pointers.TargetFileObject);

#if (FX_CORE_MODE == FX_CORE_USER_MODE)
        CloseWdfFileObject(pointers.TargetFileObject);
#endif
    }

#if (FX_CORE_MODE == FX_CORE_USER_MODE)
        UnbindHandle(&pointers);
#endif

    if (pointers.TargetHandle != NULL) {
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
            "WDFIOTARGET %p closing handle %p on close",
            GetObjectHandle(), pointers.TargetHandle);
        Mx::MxClose(pointers.TargetHandle);
    }
}
//
// Return the FxFileObject* for the given WDM PFILE_OBJECT
//
_Must_inspect_result_
NTSTATUS
FxFileObject::_GetFileObjectFromWdm(
    __in  FxDevice*                   pDevice,
    __in  WDF_FILEOBJECT_CLASS        FileObjectClass,
    __in_opt  MdFileObject            pWdmFileObject,
    __deref_out_opt FxFileObject**    ppFxFileObject
    )
{
    FxFileObject* pfo = NULL;
    PFX_DRIVER_GLOBALS FxDriverGlobals = pDevice->GetDriverGlobals();
    WDF_FILEOBJECT_CLASS normalizedFileClass;

    //
    // Normalize file object class value.
    //
    normalizedFileClass = FxFileObjectClassNormalize(FileObjectClass);

    //
    // No FileObject support
    //
    if( normalizedFileClass == WdfFileObjectNotRequired ) {
        *ppFxFileObject = NULL;
        return STATUS_SUCCESS;
    }

    if( pWdmFileObject == NULL ) {

        //
        // Warn if an I/O request has NULL for the WDM PFILE_OBJECT
        //
        if ( pDevice->IsExclusive() &&
             normalizedFileClass == WdfFileObjectWdfCannotUseFsContexts ) {
            //
            // We allow a NULL file object iff the device is exclusive and
            // we have to look up the WDFFILEOBJECT by PFILE_OBJECT value
            //
            DO_NOTHING();
        }
        else if ( FxIsFileObjectOptional(FileObjectClass) ) {
            //
            // Driver told us that it's able to handle this case.
            //
            *ppFxFileObject = NULL;
            return STATUS_SUCCESS;
        }
        else {
            DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
                                    "NULL passed for PFILE_OBJECT when FileObject "
                                    "support is requested in an I/O request");
            FxVerifierDbgBreakPoint(pDevice->GetDriverGlobals());

            return STATUS_UNSUCCESSFUL;
        }
    }

    //
    // Depending on the drivers configuration, we can quickly
    // get the FxFileObject* from FxContext, or FxContext2.
    //
    // Some drivers can not touch either of the FxContext(s), and
    // in that case we must resort to a list or hashtable.
    //
    MxFileObject wdmFileObject(pWdmFileObject);
    if( normalizedFileClass == WdfFileObjectWdfCanUseFsContext ) {
        pfo = (FxFileObject*)wdmFileObject.GetFsContext();
    }
    else if( normalizedFileClass == WdfFileObjectWdfCanUseFsContext2 ) {
        pfo = (FxFileObject*)wdmFileObject.GetFsContext2();
    }
    else {
        PLIST_ENTRY next;
        FxFileObject* f;
        KIRQL irql;

        //
        // Must look it up from the FxDevice->m_FileObjectListHead.
        //
        pfo = NULL;

        pDevice->Lock(&irql);

        next = pDevice->m_FileObjectListHead.Flink;

        if(pWdmFileObject == NULL) {
            //
            // If the pWdmFileObject is NULL then we will pass the first entry
            // in the list because the device must be exclusive and there
            // can be only one fileobject in the list.
            //
            ASSERT(IsListEmpty(&pDevice->m_FileObjectListHead) == FALSE);

            f = CONTAINING_RECORD(next, FxFileObject, m_Link);
            pfo = f;

        } else {

            while( next != &pDevice->m_FileObjectListHead ) {
                f = CONTAINING_RECORD(next, FxFileObject, m_Link);

                if( f->m_FileObject.GetFileObject()== pWdmFileObject ) {
                    pfo = f;
                    break;
                }

                next = next->Flink;
            }
        }










        if(pfo == NULL
             && pDevice->IsExclusive()
             && pDevice->GetMxDeviceObject()->GetDeviceType() == FILE_DEVICE_SERIAL_PORT
             && !IsListEmpty(&pDevice->m_FileObjectListHead)) {

            f = CONTAINING_RECORD(pDevice->m_FileObjectListHead.Flink,
                                    FxFileObject, m_Link);
            pfo = f;

            DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGDEVICE,
                            "The PFILE_OBJECT 0x%p in this request (cleanup/close) "
                            "is different from the one specified in "
                            "create request 0x%p.This is bad!", pWdmFileObject,
                            ((f != NULL) ? f->m_FileObject.GetFileObject(): NULL));
            DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGDEVICE,
                            "Since this is a serial port device, framework is "
                            "using a workaround to allow this");
        }

        pDevice->Unlock(irql);
    }

    //
    // This can happen if a different PFILE_OBJECT is passed to an I/O
    // request than was presented to IRP_MJ_CREATE
    //
    if (pfo == NULL && FxIsFileObjectOptional(FileObjectClass) == FALSE) {

        DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
                            "Could not locate WDFFILEOBJECT for "
                            "PFILE_OBJECT 0x%p",pWdmFileObject);

        DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
                            "Did a different PFILE_OBJECT get passed to the "
                            "request than was to IRP_MJ_CREATE?");










        if (FxDriverGlobals->IsVersionGreaterThanOrEqualTo(1,9)) {
            FxVerifierDbgBreakPoint(pDevice->GetDriverGlobals());
        }
    }

    //
    // We don't do an extra reference count since the file objects
    // lifetime is controlled by the IoMgr. When the IRP_MJ_CLOSE
    // occurs, the reference is released after the optional
    // driver event callback.
    //

    *ppFxFileObject = pfo;

    return STATUS_SUCCESS;
}