⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 irpqueue.c

📁 鼠标Windows驱动
💻 C
字号:
#include "pch.h"

VOID
P2InitIrpQueueContext(
    IN PIRPQUEUE_CONTEXT IrpQueueContext
    )
{
    InitializeListHead( &IrpQueueContext->irpQueue );
    KeInitializeSpinLock( &IrpQueueContext->irpQueueSpinLock );
}

VOID
P2CancelQueuedIrp(
    IN  PIRPQUEUE_CONTEXT  IrpQueueContext,
    IN  PIRP               Irp
    )
{
    KIRQL oldIrql;
    
    // Release the global cancel spin lock.  Do this while not holding
    //   any other spin locks so that we exit at the right IRQL.
    IoReleaseCancelSpinLock( Irp->CancelIrql );

    //
    //  Dequeue and complete the IRP.  The enqueue and dequeue
    //    functions synchronize properly so that if this cancel routine
    //    is called, the dequeue is safe and only the cancel routine
    //    will complete the IRP. Hold the spin lock for the IRP queue
    //    while we do this.
    //
    KeAcquireSpinLock( &IrpQueueContext->irpQueueSpinLock, &oldIrql );
    RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
    KeReleaseSpinLock( &IrpQueueContext->irpQueueSpinLock, oldIrql);
    
    //  Complete the IRP.  This is a call outside the driver, so all
    //    spin locks must be released by this point.
    P4CompleteRequest( Irp, STATUS_CANCELLED, 0 );
    return;
}

NTSTATUS 
P2QueueIrp(
    IN  PIRP               Irp,
    IN  PIRPQUEUE_CONTEXT  IrpQueueContext,
    IN  PDRIVER_CANCEL     CancelRoutine
    )
{
    PDRIVER_CANCEL  oldCancelRoutine;
    KIRQL           oldIrql;
    NTSTATUS        status = STATUS_PENDING;
    
    KeAcquireSpinLock( &IrpQueueContext->irpQueueSpinLock, &oldIrql );
    
    // Queue the IRP and call IoMarkIrpPending to indicate that the
    //   IRP may complete on a different thread.
    //
    // N.B. It's okay to call these inside the spin lock because
    //   they're macros, not functions.
    IoMarkIrpPending( Irp );
    InsertTailList( &IrpQueueContext->irpQueue, &Irp->Tail.Overlay.ListEntry );
    
    // Must set a Cancel routine before checking the Cancel flag.
    #pragma warning( push ) 
    #pragma warning( disable : 4054 4055 )
    oldCancelRoutine = IoSetCancelRoutine( Irp, CancelRoutine );
    #pragma warning( pop ) 
    ASSERT( !oldCancelRoutine );

    if( Irp->Cancel ){
        // The IRP was canceled.  Check whether our cancel routine was called.
        #pragma warning( push ) 
        #pragma warning( disable : 4054 4055 )
        oldCancelRoutine = IoSetCancelRoutine( Irp, NULL );
        #pragma warning( pop ) 

        if( oldCancelRoutine ) {
            // The cancel routine was NOT called.  
            //   So dequeue the IRP now and complete it after releasing the spinlock.
            RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
            status = Irp->IoStatus.Status = STATUS_CANCELLED; 
        }
        else {
            // The cancel routine WAS called.  As soon as we drop our
            //   spin lock it will dequeue and complete the IRP.  So
            //   leave the IRP in the queue and otherwise don't touch
            //   it.  Return pending since we're not completing the IRP
            //   here.
        }
    }
    
    KeReleaseSpinLock(&IrpQueueContext->irpQueueSpinLock, oldIrql);
    
    // Normally you shouldn't call IoMarkIrpPending and return a
    //   status other than STATUS_PENDING.  But you can break this rule
    //   if you complete the IRP.
    if( status != STATUS_PENDING ) {
        P4CompleteRequest( Irp, Irp->IoStatus.Status, Irp->IoStatus.Information );
    }
    return status;
}

PIRP
P2DequeueIrp(
    IN  PIRPQUEUE_CONTEXT IrpQueueContext,
    IN  PDRIVER_CANCEL    CancelRoutine
    )
{
    KIRQL oldIrql;
    PIRP  nextIrp = NULL;

    KeAcquireSpinLock( &IrpQueueContext->irpQueueSpinLock, &oldIrql );

    while( !nextIrp && !IsListEmpty( &IrpQueueContext->irpQueue ) ){

        PDRIVER_CANCEL oldCancelRoutine;

        PLIST_ENTRY listEntry = RemoveHeadList( &IrpQueueContext ->irpQueue );
        
        // Get the next IRP off the queue.
        nextIrp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
        
        //  Clear the IRP's cancel routine
        #pragma warning( push ) 
        #pragma warning( disable : 4054 4055 )
        oldCancelRoutine = IoSetCancelRoutine( nextIrp, NULL );
        #pragma warning( pop )

        //  IoCancelIrp() could have just been called on this IRP.
        //    What we're interested in is not whether IoCancelIrp() was called (nextIrp->Cancel flag set),
        //    but whether IoCancelIrp() called (or is about to call) our cancel routine.
        //    To check that, check the result of the test-and-set macro IoSetCancelRoutine.
        if( oldCancelRoutine ) {
            //  Cancel routine not called for this IRP.  Return this IRP.
            #if DBG
            ASSERT( oldCancelRoutine == CancelRoutine );
            #else
            UNREFERENCED_PARAMETER( CancelRoutine );
            #endif
        } else {
            //  This IRP was just canceled and the cancel routine was (or will be) called.
            //  The cancel routine will complete this IRP as soon as we drop the spin lock,
            //  so don't do anything with the IRP.
            //  Also, the cancel routine will try to dequeue the IRP, 
            //  so make the IRP's listEntry point to itself.
            ASSERT( nextIrp->Cancel );
            InitializeListHead( &nextIrp->Tail.Overlay.ListEntry );
            nextIrp = NULL;
        }
    }
    
    KeReleaseSpinLock( &IrpQueueContext ->irpQueueSpinLock, oldIrql );
    
    return nextIrp;
}

VOID
P2CancelRoutine(
    IN  PDEVICE_OBJECT  DevObj,
    IN  PIRP            Irp
    )
// this routine is driver specific - most other routines in this file are generic
{
    PFDO_EXTENSION     fdx             = DevObj->DeviceExtension;
    PIRPQUEUE_CONTEXT  irpQueueContext = &fdx->IrpQueueContext;
    P2CancelQueuedIrp( irpQueueContext, Irp );
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -