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; }