Sample Code

Windows Driver Samples/ PCIDRV - WDF Driver for PCI Device/ C++/ kmdf/ HW/ isrdpc.c/

/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    ISRDPC.C

Abstract:

    Contains routine to handle interrupts, interrupt DPCs and WatchDogTimer DPC

Environment:

    Kernel mode

--*/

#include "precomp.h"

#if defined(EVENT_TRACING)
#include "ISRDPC.tmh"
#endif

#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, NICEvtDeviceD0ExitPreInterruptsDisabled)
#endif


BOOLEAN
NICEvtInterruptIsr(
    IN WDFINTERRUPT Interrupt,
    IN ULONG        MessageID
    )
/*++
Routine Description:

    Interrupt handler for the device.

Arguments:

    Interupt - Address of the framework interrupt object
    MessageID -

Return Value:

     TRUE if our device is interrupting, FALSE otherwise.

--*/
{
    BOOLEAN    InterruptRecognized = FALSE;
    PFDO_DATA  FdoData = NULL;
    USHORT     IntStatus;

    UNREFERENCED_PARAMETER( MessageID );

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "--> NICEvtInterruptIsr\n");

    FdoData = FdoGetData(WdfInterruptGetDevice(Interrupt));

    //
    // We process the interrupt if it's not disabled and it's active
    //
    if (!NIC_INTERRUPT_DISABLED(FdoData) && NIC_INTERRUPT_ACTIVE(FdoData))
    {
        InterruptRecognized = TRUE;

        //
        // Disable the interrupt (will be re-enabled in NICEvtInterruptDpc
        //
        NICDisableInterrupt(FdoData);

        //
        // Acknowledge the interrupt(s) and get the interrupt status
        //

        NIC_ACK_INTERRUPT(FdoData, IntStatus);

        WdfInterruptQueueDpcForIsr( Interrupt );

    }

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "<-- NICEvtInterruptIsr\n");

    return InterruptRecognized;
}

VOID
NICEvtInterruptDpc(
    IN WDFINTERRUPT WdfInterrupt,
    IN WDFOBJECT    WdfDevice
    )

/*++

Routine Description:

    DPC callback for ISR.

Arguments:

    WdfInterrupt - Handle to the framework interrupt object

    WdfDevice - Associated device object.

Return Value:

--*/
{
    PFDO_DATA fdoData = NULL;

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "--> NICEvtInterruptDpc\n");

    fdoData = FdoGetData(WdfDevice);


    WdfSpinLockAcquire(fdoData->RcvLock);

    NICHandleRecvInterrupt(fdoData);


    WdfSpinLockRelease(fdoData->RcvLock);

    //
    // Handle send interrupt
    //

    WdfSpinLockAcquire(fdoData->SendLock);

    NICHandleSendInterrupt(fdoData);


    WdfSpinLockRelease(fdoData->SendLock);

    //
    // Check if any queued Sends need to be reprocessed.
    //
    NICCheckForQueuedSends(fdoData);

    //
    // Start the receive unit if it had stopped
    //

    WdfSpinLockAcquire(fdoData->RcvLock);

    NICStartRecv(fdoData);


    WdfSpinLockRelease(fdoData->RcvLock);

    //
    // Re-enable the interrupt (disabled in MPIsr)
    //
    WdfInterruptSynchronize(
        WdfInterrupt,
        NICEnableInterrupt,
        fdoData);

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "<-- NICEvtInterruptDpc\n");

}

NTSTATUS
NICEvtInterruptEnable(
    IN WDFINTERRUPT  Interrupt,
    IN WDFDEVICE     AssociatedDevice
    )
/*++

Routine Description:

    This event is called when the Framework moves the device to D0, and after
    EvtDeviceD0Entry.  The driver should enable its interrupt here.

    This function will be called at the device's assigned interrupt
    IRQL (DIRQL.)

Arguments:

    Interrupt - Handle to a Framework interrupt object.

    AssociatedDevice - Handle to a Framework device object.

Return Value:

    BOOLEAN - TRUE indicates that the interrupt was successfully enabled.

--*/
{
    PFDO_DATA           fdoData;

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> NICEvtInterruptEnable\n");

    fdoData = FdoGetData(AssociatedDevice);
    NICEnableInterrupt(Interrupt, fdoData);

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- NICEvtInterruptEnable\n");

    return STATUS_SUCCESS;
}

NTSTATUS
NICEvtInterruptDisable(
    IN WDFINTERRUPT  Interrupt,
    IN WDFDEVICE     AssociatedDevice
    )
/*++

Routine Description:

    This event is called before the Framework moves the device to D1, D2 or D3
    and before EvtDeviceD0Exit.  The driver should disable its interrupt here.

    This function will be called at the device's assigned interrupt
    IRQL (DIRQL.)

Arguments:

    Interrupt - Handle to a Framework interrupt object.

    AssociatedDevice - Handle to a Framework device object.

Return Value:

    STATUS_SUCCESS -  indicates success.

--*/
{
    PFDO_DATA           fdoData;

    UNREFERENCED_PARAMETER(Interrupt);
    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> NICEvtInterruptDisable\n");

    fdoData = FdoGetData(AssociatedDevice);
    NICDisableInterrupt(fdoData);

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- NICEvtInterruptDisable\n");

    return STATUS_SUCCESS;
}

NTSTATUS
NICEvtDeviceD0EntryPostInterruptsEnabled(
    IN WDFDEVICE     Device,
    IN WDF_POWER_DEVICE_STATE PreviousState
    )
/*++

Routine Description:

    This event is called so that driver can do PASSIVE_LEVEL work after
    the interrupt is connected and enabled. Here we start the watchdog timer.
    Watch dog timer is used to do the initial link detection during
    start and then used to make sure the device is not stuck for any reason.

    This function is not marked pageable because this function is in the
    device power up path. When a function is marked pagable and the code
    section is paged out, it will generate a page fault which could impact
    the fast resume behavior because the client driver will have to wait
    until the system drivers can service this page fault.

Arguments:

    Interrupt - Handle to a Framework interrupt object.

    AssociatedDevice - Handle to a Framework device object.

Return Value:

   STATUS_SUCCESS -  indicates success.

--*/
{
    PFDO_DATA  fdoData;

    UNREFERENCED_PARAMETER( PreviousState );

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> NICEvtDeviceD0EntryPostInterruptsEnabled\n");

    fdoData = FdoGetData(Device);

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- NICEvtDeviceD0EntryPostInterruptsEnabled\n");

    return STATUS_SUCCESS;

}

NTSTATUS
NICEvtDeviceD0ExitPreInterruptsDisabled(
    IN WDFDEVICE     Device,
    IN WDF_POWER_DEVICE_STATE TargetState
    )
/*++

Routine Description:

    This event is called so that driver can do PASSIVE_LEVEL work before
    the interrupt is disconnected and disabled.

Arguments:

    Interrupt - Handle to a Framework interrupt object.

    AssociatedDevice - Handle to a Framework device object.

Return Value:

    STATUS_SUCCESS -  indicates success.

--*/
{
    PFDO_DATA           fdoData;

    UNREFERENCED_PARAMETER(TargetState);

    PAGED_CODE();

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> NICEvtDeviceD0ExitPreInterruptsDisabled\n");

    fdoData = FdoGetData(Device);

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- NICEvtDeviceD0ExitPreInterruptsDisabled\n");

    return STATUS_SUCCESS;

}

VOID
NICStartWatchDogTimer(
    IN  PFDO_DATA     FdoData
    )
{
    LARGE_INTEGER           dueTime;

    if(!FdoData->CheckForHang){

        //
        // Set the link detection flag to indicate that NICWatchDogEvtTimerFunc
        // is first doing link-detection.
        //
        MP_SET_FLAG(FdoData, fMP_ADAPTER_LINK_DETECTION);
        FdoData->CheckForHang = FALSE;
        FdoData->bLinkDetectionWait = FALSE;
        FdoData->bLookForLink = FALSE;
        dueTime.QuadPart = NIC_LINK_DETECTION_DELAY;

    } else {
        dueTime.QuadPart = NIC_CHECK_FOR_HANG_DELAY;
    }

    WdfTimerStart(FdoData->WatchDogTimer,
                    dueTime.QuadPart
                    );

}

VOID
NICWatchDogEvtTimerFunc(
    IN WDFTIMER Timer
    )
/*++

Routine Description:

    This DPC is used to do both link detection during hardware init and
    after that for hardware hang detection.

Arguments:


Return Value:

    None

--*/
{
    PFDO_DATA           FdoData = NULL;
    LARGE_INTEGER       DueTime;
    NTSTATUS            status = STATUS_SUCCESS;

    FdoData = FdoGetData(WdfTimerGetParentObject(Timer));

    DueTime.QuadPart = NIC_CHECK_FOR_HANG_DELAY;


    if(!FdoData->CheckForHang){
        //
        // We are still doing link detection
        //
        status = NICLinkDetection(FdoData);
        if(status == STATUS_PENDING) {
            // Wait for 100 ms
            FdoData->bLinkDetectionWait = TRUE;
            DueTime.QuadPart = NIC_LINK_DETECTION_DELAY;
        }else {
            FdoData->CheckForHang = TRUE;
        }
    }else {
        //
        // Link detection is over, let us check to see
        // if the hardware is stuck.
        //
        if(NICCheckForHang(FdoData)){

            status = NICReset(FdoData);
            if(!NT_SUCCESS(status)){
                goto Exit;
            }
        }
    }

    WdfTimerStart(FdoData->WatchDogTimer,   // Timer
                DueTime.QuadPart                     // DueTime
                );

    return;

Exit:
    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "WatchDogTimer is exiting %x\n", status);
    return;

}

BOOLEAN
NICCheckForHang(
    IN  PFDO_DATA     FdoData
    )
/*++

Routine Description:

    CheckForHang handler is called in the context of a timer DPC.
    take advantage of this fact when acquiring/releasing spinlocks

Arguments:

    FdoData  Pointer to our adapter

Return Value:

    TRUE    This NIC needs a reset
    FALSE   Everything is fine

--*/
{
     PMP_TCB             pMpTcb;

    //
    // Just skip this part if the adapter is doing link detection
    //
    if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_LINK_DETECTION))
    {
        return(FALSE);
    }

    //
    // any nonrecoverable hardware error?
    //
    if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_NON_RECOVER_ERROR))
    {
        TraceEvents(TRACE_LEVEL_WARNING, DBG_DPC, "Non recoverable error - remove\n");
        return (TRUE);
    }

    //
    // hardware failure?
    //
    if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_HARDWARE_ERROR))
    {
        TraceEvents(TRACE_LEVEL_WARNING, DBG_DPC, "hardware error - reset\n");
        return(TRUE);
    }

    //
    // Is send stuck?
    //


    WdfSpinLockAcquire(FdoData->SendLock);

    if (FdoData->nBusySend > 0)
    {
        pMpTcb = FdoData->CurrSendHead;
        pMpTcb->Count++;
        if (pMpTcb->Count > NIC_SEND_HANG_THRESHOLD)
        {

            WdfSpinLockRelease(FdoData->SendLock);
            TraceEvents(TRACE_LEVEL_WARNING, DBG_DPC, "Send is stuck - reset\n");
            return(TRUE);
        }
    }


    WdfSpinLockRelease(FdoData->SendLock);


    WdfSpinLockAcquire(FdoData->RcvLock);

    //
    // Update the RFD shrink count
    //
    if (FdoData->CurrNumRfd > FdoData->NumRfd)
    {
        FdoData->RfdShrinkCount++;
    }


    WdfSpinLockRelease(FdoData->RcvLock);

    NICIndicateMediaState(FdoData);

    return(FALSE);
}

NTSTATUS
NICReset(
    IN PFDO_DATA FdoData
    )
/*++

Routine Description:

    Function to reset the device.

Arguments:

    FdoData  Pointer to our adapter


Return Value:

    NT Status code.

Note:
    NICReset is called at DPC. Take advantage of this fact
    when acquiring or releasing spinlocks

--*/
{
    NTSTATUS                status;

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "---> MPReset\n");


    WdfSpinLockAcquire(FdoData->Lock);

    WdfSpinLockAcquire(FdoData->SendLock);

    WdfSpinLockAcquire(FdoData->RcvLock);

    do
    {
        //
        // Is this adapter already doing a reset?
        //
        if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_RESET_IN_PROGRESS))
        {
            status = STATUS_SUCCESS;
            goto exit;
        }

        MP_SET_FLAG(FdoData, fMP_ADAPTER_RESET_IN_PROGRESS);

        //
        // Is this adapter doing link detection?
        //
        if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_LINK_DETECTION))
        {
            TraceEvents(TRACE_LEVEL_WARNING, DBG_DPC, "Reset is pended...\n");
            status = STATUS_SUCCESS;
            goto exit;
        }
        //
        // Is this adapter going to be removed
        //
        if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_NON_RECOVER_ERROR))
        {
           status = STATUS_DEVICE_DATA_ERROR;
           if (MP_TEST_FLAG(FdoData, fMP_ADAPTER_REMOVE_IN_PROGRESS))
           {
               goto exit;
           }

           // This is an unrecoverable hardware failure.
           // We need to tell NDIS to remove this miniport
           MP_SET_FLAG(FdoData, fMP_ADAPTER_REMOVE_IN_PROGRESS);
           MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_RESET_IN_PROGRESS);


           WdfSpinLockRelease(FdoData->RcvLock);

            WdfSpinLockRelease(FdoData->SendLock);

            WdfSpinLockRelease(FdoData->Lock);

           // TODO: Log an entry into the eventlog
           WdfDeviceSetFailed(FdoData->WdfDevice, WdfDeviceFailedAttemptRestart);

           TraceEvents(TRACE_LEVEL_FATAL, DBG_DPC, "<--- MPReset, status=%x\n", status);

           return status;
        }


        //
        // Disable the interrupt and issue a reset to the NIC
        //
        NICDisableInterrupt(FdoData);
        NICIssueSelectiveReset(FdoData);


        //
        // Release all the locks and then acquire back the send lock
        // we are going to clean up the send queues
        // which may involve calling Ndis APIs
        // release all the locks before grabbing the send lock to
        // avoid deadlocks
        //


         WdfSpinLockRelease(FdoData->RcvLock);

        WdfSpinLockRelease(FdoData->SendLock);

        WdfSpinLockRelease(FdoData->Lock);


        WdfSpinLockAcquire(FdoData->SendLock);

        //
        // Free the packets on SendQueueList
        //
        NICFreeQueuedSendPackets(FdoData);

        //
        // Free the packets being actively sent & stopped
        //
        NICFreeBusySendPackets(FdoData);


        RtlZeroMemory(FdoData->MpTcbMem, FdoData->MpTcbMemSize);

        //
        // Re-initialize the send structures
        //
        NICInitSendBuffers(FdoData);


        WdfSpinLockRelease(FdoData->SendLock);

        //
        // get all the locks again in the right order
        //


        WdfSpinLockAcquire(FdoData->Lock);

        WdfSpinLockAcquire(FdoData->SendLock);

        WdfSpinLockAcquire(FdoData->RcvLock);

        //
        // Reset the RFD list and re-start RU
        //
        NICResetRecv(FdoData);
        status = NICStartRecv(FdoData);
        if (status != STATUS_SUCCESS)
        {
            // Are we having failures in a few consecutive resets?
            if (FdoData->HwErrCount < NIC_HARDWARE_ERROR_THRESHOLD)
            {
                // It's not over the threshold yet, let it to continue
                FdoData->HwErrCount++;
            }
            else
            {
                // This is an unrecoverable hardware failure.
                // We need to tell NDIS to remove this miniport
                MP_SET_FLAG(FdoData, fMP_ADAPTER_REMOVE_IN_PROGRESS);
                MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_RESET_IN_PROGRESS);



                WdfSpinLockRelease(FdoData->RcvLock);

                WdfSpinLockRelease(FdoData->SendLock);

                WdfSpinLockRelease(FdoData->Lock);

                // TODO: Log an entry into the eventlog
                //
                // Tell the system that the device has failed.
                //
                WdfDeviceSetFailed(FdoData->WdfDevice, WdfDeviceFailedAttemptRestart);

                TraceEvents(TRACE_LEVEL_ERROR, DBG_DPC, "<--- MPReset, status=%x\n", status);
                return(status);
            }

            break;
        }

        FdoData->HwErrCount = 0;
        MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_HARDWARE_ERROR);

        NICEnableInterrupt(FdoData->WdfInterrupt, FdoData);

    } WHILE (FALSE);

    MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_RESET_IN_PROGRESS);

    exit:


        WdfSpinLockRelease(FdoData->RcvLock);

        WdfSpinLockRelease(FdoData->SendLock);

        WdfSpinLockRelease(FdoData->Lock);



    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "<--- MPReset, status=%x\n", status);
    return(status);
}


NTSTATUS
NICLinkDetection(
    PFDO_DATA         FdoData
    )
/*++

Routine Description:

    Timer function for postponed link negotiation. Called from
    the NICWatchDogEvtTimerFunc. After the link detection is over
    we will complete any pending ioctl or send IRPs.

Arguments:

    FdoData     Pointer to our FdoData

Return Value:

    NT status

--*/
{
    NTSTATUS                status = STATUS_SUCCESS;
    MEDIA_STATE             CurrMediaState;
    PNDISPROT_QUERY_OID     pQuery = NULL;
    PNDISPROT_SET_OID       pSet = NULL;
    PVOID                   DataBuffer;
    ULONG                   BytesWritten;
    NDIS_OID                Oid;
    PVOID                   InformationBuffer;
    size_t               bufSize;
    WDFREQUEST              request;

    //
    // Handle the link negotiation.
    //
    if (FdoData->bLinkDetectionWait)
    {
        status = ScanAndSetupPhy(FdoData);
    }
    else
    {
        status = PhyDetect(FdoData);
    }

    if (status == STATUS_PENDING)
    {
        return status;
    }

    //
    // Reset some variables for link detection
    //
    FdoData->bLinkDetectionWait = FALSE;

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "NICLinkDetection - negotiation done\n");


    WdfSpinLockAcquire(FdoData->Lock);
    MP_CLEAR_FLAG(FdoData, fMP_ADAPTER_LINK_DETECTION);

    WdfSpinLockRelease(FdoData->Lock);

    //
    // Any OID query request pending?
    //

    status = NICGetIoctlRequest(FdoData->PendingIoctlQueue,
                                IOCTL_NDISPROT_QUERY_OID_VALUE,
                                &request);

    if(NT_SUCCESS(status)) {
        status = WdfRequestRetrieveOutputBuffer(request, sizeof(NDISPROT_QUERY_OID), &DataBuffer, &bufSize);
        if(NT_SUCCESS(status)) {

            pQuery = (PNDISPROT_QUERY_OID)DataBuffer;
            Oid = pQuery->Oid;
            InformationBuffer = &pQuery->Data[0];
            switch(Oid)
            {
                case OID_GEN_LINK_SPEED:
                    *((PULONG)InformationBuffer) = FdoData->usLinkSpeed * 10000;
                    BytesWritten = sizeof(ULONG);

                    break;

                case OID_GEN_MEDIA_CONNECT_STATUS:
                default:
                    ASSERT(Oid == OID_GEN_MEDIA_CONNECT_STATUS);

                    CurrMediaState = NICIndicateMediaState(FdoData);

                    RtlMoveMemory(InformationBuffer,
                                  &CurrMediaState,
                                  sizeof(NDIS_MEDIA_STATE));

                    BytesWritten = sizeof(NDIS_MEDIA_STATE);
            }

            WdfRequestCompleteWithInformation(request, status, BytesWritten);
        }
    }

    //
    // Any OID set request pending?
    //
    status = NICGetIoctlRequest(FdoData->PendingIoctlQueue,
                            IOCTL_NDISPROT_SET_OID_VALUE,
                             &request);

    if(NT_SUCCESS(status)) {
        ULONG    PacketFilter;

        status = WdfRequestRetrieveOutputBuffer(request, sizeof(NDISPROT_SET_OID), &DataBuffer, &bufSize);
        if(NT_SUCCESS(status)) {

            pSet = (PNDISPROT_SET_OID)DataBuffer;
            Oid = pSet->Oid;
            InformationBuffer = &pSet->Data[0];
            if (Oid == OID_GEN_CURRENT_PACKET_FILTER)
            {

                RtlMoveMemory(&PacketFilter, InformationBuffer, sizeof(ULONG));


                WdfSpinLockAcquire(FdoData->Lock);

                status = NICSetPacketFilter(
                             FdoData,
                             PacketFilter);


                WdfSpinLockRelease(FdoData->Lock);

                if (status == STATUS_SUCCESS)
                {
                    FdoData->PacketFilter = PacketFilter;
                }

                WdfRequestCompleteWithInformation(request, status, 0);
            }
        }
    }

    //
    // Any read pending?
    //

    WdfSpinLockAcquire(FdoData->RcvLock);

    //
    // Start the NIC receive unit
    //
    status = NICStartRecv(FdoData);
    if (status != STATUS_SUCCESS)
    {
        MP_SET_HARDWARE_ERROR(FdoData);
    }


    WdfSpinLockRelease(FdoData->RcvLock);

    //
    // Send packets which have been queued while link detection was going on.
    //
    NICCheckForQueuedSends(FdoData);

    return status;
}


Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS