VOID
FxWorkItem::WorkItemThunk(
    __in MdDeviceObject DeviceObject,
    __in_opt PVOID      Context
    )
/*++

Routine Description:

    This is the static routine called by the kernels PIO_WORKITEM handler.

    A reference count was taken by Enqueue, which this routine releases
    on return.

Arguments:
    DeviceObject  - the devobj we passed to IoCreateWorkItem

    Context - the internal Fx object

Return Value:
    None

--*/
{
    FxWorkItem*         pWorkItem;
    PFX_DRIVER_GLOBALS  pFxDriverGlobals;

    UNREFERENCED_PARAMETER(DeviceObject);

    pWorkItem = (FxWorkItem*)Context;
    pFxDriverGlobals = pWorkItem->GetDriverGlobals();

    //
    // Save the current worker thread object pointer. We will use this avoid
    // deadlock if the driver tries to delete or flush the workitem from within
    // the callback.
    //
    pWorkItem->m_WorkItemThread = Mx::MxGetCurrentThread();

    pWorkItem->WorkItemHandler();

    pWorkItem->m_WorkItemThread = NULL;

    //
    // Release the reference on the FxWorkItem and Globals taken when Enqueue
    // was done. This may release the FxWorkItem if it is running down.
    //
    pWorkItem->RELEASE(WorkItemThunk);

    //
    // This may release the driver if it is running down.
    //
    pFxDriverGlobals->RELEASE(WorkItemThunk);
}
Beispiel #2
0
NTSTATUS
WDFEXPORT(WdfDeviceWdmDispatchPreprocessedIrp)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFDEVICE Device,
    __in
    MdIrp Irp
    )
{
    FxDevice            *device;
    PFX_DRIVER_GLOBALS  fxDriverGlobals;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   Device,
                                   FX_TYPE_DEVICE,
                                   (PVOID*) &device,
                                   &fxDriverGlobals);

    FxPointerNotNull(fxDriverGlobals, Irp);
    
    //
    // Verifier checks. 
    // This API can only be called by the client driver, Cx must call 
    // WdfDeviceWdmDispatchIrp from its preprocess callback.
    // Also, Cx must register for a Preprocessor routine using 
    // WdfCxDeviceInitAssignWdmIrpPreprocessCallback.
    //
    if (fxDriverGlobals->IsVerificationEnabled(1, 11, OkForDownLevel)) {
        if (device->IsCxInIoPath()) {
            FxDriver* driver = GetFxDriverGlobals(DriverGlobals)->Driver;
            
            if (IsListEmpty(&device->m_PreprocessInfoListHead) ||
                device->IsCxDriverInIoPath(driver)) {

                DoTraceLevelMessage(
                        fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
                        "This API can only be called by client driver from its "
                        "pre-process IRP callback, STATUS_INVALID_DEVICE_REQUEST");
                FxVerifierDbgBreakPoint(fxDriverGlobals);
            }
        }
    }

    //
    // OK, ready to dispatch IRP.
    //
    return device->DispatchPreprocessedIrp(
                        Irp, 
                        device->m_PreprocessInfoListHead.Flink->Flink);
}
VOID
FxWorkItem::Enqueue(
    VOID
    )
{
    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    KIRQL irql;
    BOOLEAN enqueue;

    pFxDriverGlobals = GetDriverGlobals();
    enqueue = FALSE;

    Lock(&irql);

    if (m_Enqueued) {
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDEVICE,
            "Previously queued WDFWORKITEM 0x%p is already pending. "
            "Ignoring the request to queue again", GetHandle());
    }
    else if (m_RunningDown) {
        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
                "WDFWORKITEM 0x%p is already deleted", GetHandle());
        FxVerifierDbgBreakPoint(pFxDriverGlobals);
    }
    else {
        m_WorkItemCompleted.Clear();

        m_Enqueued = TRUE;

        //
        // We are going to enqueue the work item. Reference this FxWorkItem 
        // object and Globals while they are outstanding.
        // These will be released when the workitem completes.
        //
        ADDREF(WorkItemThunk);
        pFxDriverGlobals->ADDREF(WorkItemThunk);

        enqueue = TRUE;
    }

    Unlock(irql);

    if (enqueue) {
        m_WorkItem. Enqueue(FxWorkItem::WorkItemThunk, this);
    }

    return;
}
VOID
FxPowerIdleMachine::IoDecrement(
    __in_opt PVOID Tag,
    __in_opt LONG Line,
    __in_opt PSTR File
    )
/*++

Routine Description:
    Public function which allows the caller decrement the pending io count on
    this state machine.  If the count goes to zero and idle is enabled, then
    the timer is started.

Arguments:
    None

Return Value:
    None

  --*/
{
    KIRQL irql;
    FxPkgPnp* pPkgPnp;
    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    ULONG count;

    pPkgPnp = GetPnpPkg(this);
    pFxDriverGlobals = pPkgPnp->GetDriverGlobals();

    m_Lock.Acquire(&irql);

    if (m_IoCount == 0) {
        //
        // We can get here for the following reasons:
        // 1. Driver called WdfDevicveStopIdle/WdfDeviceResumeIdle in a mismatched
        //    manner. This is a driver bug.
        // 2. Framework did power deref without a corresponding power ref.
        //    This would be a framework bug. 
        //
        // We will break into debugger if verifier is turned on. This will allow
        // developers to catch this problem during develeopment.
        // We limit this break to version 1.11+ because otherwise older drivers 
        // may hit this, and if they cannot be fixed for some reason, then 
        // verifier would need to be turned off to avoid the break which is not 
        // desirable. 
        // 
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
            "WDFDEVICE 0x%p !devobj 0x%p The device is being power-dereferenced"
            " without a matching power-reference. This could occur if driver"
            " incorrectly calls WdfDeviceResumeIdle without a matching call to"
            " WdfDeviceStopIdle.",
            pPkgPnp->GetDevice()->GetHandle(),
            pPkgPnp->GetDevice()->GetDeviceObject());
        
        if (pFxDriverGlobals->IsVerificationEnabled(1, 11, OkForDownLevel)) {
           FxVerifierDbgBreakPoint(pFxDriverGlobals);
        }
    }

    ASSERT(m_IoCount > 0);
    count = --m_IoCount;
    ProcessEventLocked(PowerIdleEventIoDecrement);
    m_Lock.Release(irql);

    if (m_TagTracker != NULL) {
        m_TagTracker->UpdateTagHistory(Tag, Line, File, TagRelease, count);
    }
}
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;
}