Sample Code

Windows Driver Samples/ NDIS Virtual Miniport Driver/ C++/ adapter.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:

   Adapter.C

Abstract:

    The purpose of this sample is to illustrate functionality of a deserialized
    NDIS miniport driver without requiring a physical network adapter. This
    sample is based on E100BEX sample present in the DDK. It is basically a
    simplified version of E100bex driver. The driver can be installed either
    manually using Add Hardware wizard as a root enumerated virtual miniport
    driver or on a virtual bus (like toaster bus). Since the driver does not
    interact with any hardware, it makes it very easy to understand the miniport
    interface and the usage of various NDIS functions without the clutter of
    hardware specific code normally found in a fully functional driver.

    This sample provides an example of minimal driver intended for education
    purposes. Neither the driver or its sample test programs are intended
    for use in a production environment.

Revision History:

Notes:

--*/

#include "netvmin6.h"
#include "adapter.tmh"

static
NDIS_STATUS
NICAllocAdapter(
    _In_ NDIS_HANDLE MiniportAdapterHandle,
    _Outptr_ PMP_ADAPTER *Adapter);

void
NICFreeAdapter(
    _In_  PMP_ADAPTER Adapter);

VOID
NICFreeReceiveDpc(
    _In_ PMP_ADAPTER_RECEIVE_DPC AdapterDpc);

static
NDIS_STATUS
NICReadRegParameters(
    _In_  PMP_ADAPTER Adapter);

static
VOID
NICSetMacAddress(
    _In_  PMP_ADAPTER  Adapter,
    _In_  NDIS_HANDLE  ConfigurationHandle);


static
VOID
NICScheduleTheResetOrPauseDpc(
    _In_  PMP_ADAPTER  Adapter,
    _In_  BOOLEAN      fReschedule);

NDIS_TIMER_FUNCTION NICAsyncResetOrPauseDpc;

#pragma NDIS_PAGEABLE_FUNCTION(MPInitializeEx)
#pragma NDIS_PAGEABLE_FUNCTION(MPPause)
#pragma NDIS_PAGEABLE_FUNCTION(MPRestart)
#pragma NDIS_PAGEABLE_FUNCTION(MPHaltEx)
#pragma NDIS_PAGEABLE_FUNCTION(MPDevicePnpEventNotify)
#pragma NDIS_PAGEABLE_FUNCTION(NICAllocAdapter)
#pragma NDIS_PAGEABLE_FUNCTION(NICReadRegParameters)
#pragma NDIS_PAGEABLE_FUNCTION(NICSetMacAddress)



NDIS_OID NICSupportedOids[] =
{
        OID_GEN_HARDWARE_STATUS,
        OID_GEN_TRANSMIT_BUFFER_SPACE,
        OID_GEN_RECEIVE_BUFFER_SPACE,
        OID_GEN_TRANSMIT_BLOCK_SIZE,
        OID_GEN_RECEIVE_BLOCK_SIZE,
        OID_GEN_VENDOR_ID,
        OID_GEN_VENDOR_DESCRIPTION,
        OID_GEN_VENDOR_DRIVER_VERSION,
        OID_GEN_CURRENT_PACKET_FILTER,
        OID_GEN_CURRENT_LOOKAHEAD,
        OID_GEN_DRIVER_VERSION,
        OID_GEN_MAXIMUM_TOTAL_SIZE,
        OID_GEN_XMIT_OK,
        OID_GEN_RCV_OK,
        OID_GEN_STATISTICS,
        OID_GEN_TRANSMIT_QUEUE_LENGTH,       // Optional
        OID_GEN_LINK_PARAMETERS,
        OID_GEN_INTERRUPT_MODERATION,
        OID_GEN_MEDIA_SUPPORTED,
        OID_GEN_MEDIA_IN_USE,
        OID_GEN_MAXIMUM_SEND_PACKETS,
        OID_GEN_XMIT_ERROR,
        OID_GEN_RCV_ERROR,
        OID_GEN_RCV_NO_BUFFER,
        OID_802_3_PERMANENT_ADDRESS,
        OID_802_3_CURRENT_ADDRESS,
        OID_802_3_MULTICAST_LIST,
        OID_802_3_MAXIMUM_LIST_SIZE,
        OID_802_3_RCV_ERROR_ALIGNMENT,
        OID_802_3_XMIT_ONE_COLLISION,
        OID_802_3_XMIT_MORE_COLLISIONS,
        OID_802_3_XMIT_DEFERRED,             // Optional
        OID_802_3_XMIT_MAX_COLLISIONS,       // Optional
        OID_802_3_RCV_OVERRUN,               // Optional
        OID_802_3_XMIT_UNDERRUN,             // Optional
        OID_802_3_XMIT_HEARTBEAT_FAILURE,    // Optional
        OID_802_3_XMIT_TIMES_CRS_LOST,       // Optional
        OID_802_3_XMIT_LATE_COLLISIONS,      // Optional
        OID_PNP_CAPABILITIES,                // Optional
 #if (NDIS_SUPPORT_NDIS620)
        OID_RECEIVE_FILTER_ALLOCATE_QUEUE,
        OID_RECEIVE_FILTER_QUEUE_ALLOCATION_COMPLETE,
        OID_RECEIVE_FILTER_FREE_QUEUE,
        OID_RECEIVE_FILTER_CLEAR_FILTER,
        OID_RECEIVE_FILTER_SET_FILTER,
#endif
};


NDIS_STATUS
MPInitializeEx(
    _In_  NDIS_HANDLE MiniportAdapterHandle,
    _In_  NDIS_HANDLE MiniportDriverContext,
    _In_  PNDIS_MINIPORT_INIT_PARAMETERS MiniportInitParameters)
/*++
Routine Description:

    The MiniportInitialize function is a required function that sets up a
    NIC (or virtual NIC) for network I/O operations, claims all hardware
    resources necessary to the NIC in the registry, and allocates resources
    the driver needs to carry out network I/O operations.

    MiniportInitialize runs at IRQL = PASSIVE_LEVEL.

Arguments:

    Return Value:

    NDIS_STATUS_xxx code

--*/
{
    NDIS_STATUS          Status = NDIS_STATUS_SUCCESS;
    PMP_ADAPTER Adapter = NULL;


    DEBUGP(MP_TRACE, "---> MPInitializeEx\n");
    UNREFERENCED_PARAMETER(MiniportDriverContext);
    PAGED_CODE();


    do
    {
        NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES AdapterRegistration = {0};
        NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES AdapterGeneral = {0};

#if (NDIS_SUPPORT_NDIS620)
        NDIS_PM_CAPABILITIES PmCapabilities;
#elif (NDIS_SUPPORT_NDIS6)
        NDIS_PNP_CAPABILITIES PnpCapabilities;
#endif // NDIS MINIPORT VERSION

        //
        // Allocate adapter context structure and initialize all the
        // memory resources for sending and receiving packets.
        //
        Status = NICAllocAdapter(MiniportAdapterHandle, &Adapter);
        if(Status != NDIS_STATUS_SUCCESS)
        {
            break;
        }

        DEBUGP(MP_TRACE, "[%p] MPInitializeEx Adapter allocated.\n", Adapter);

        //
        // First, set the registration attributes.
        //
#if (NDIS_SUPPORT_NDIS630)
        {C_ASSERT(sizeof(AdapterRegistration) >= NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_2);}
        AdapterRegistration.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES;
        AdapterRegistration.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_2;
        AdapterRegistration.Header.Revision = NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_2;
#else
        {C_ASSERT(sizeof(AdapterRegistration) >= NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1);}
        AdapterRegistration.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES;
        AdapterRegistration.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1;
        AdapterRegistration.Header.Revision = NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1;
#endif // NDIS MINIPORT VERSION

        AdapterRegistration.MiniportAdapterContext = Adapter;
        AdapterRegistration.AttributeFlags = NIC_ADAPTER_ATTRIBUTES_FLAGS;

#if (NDIS_SUPPORT_NDIS630)
        AdapterRegistration.AttributeFlags |= NDIS_MINIPORT_ATTRIBUTES_NO_PAUSE_ON_SUSPEND;
#endif

        AdapterRegistration.CheckForHangTimeInSeconds = NIC_ADAPTER_CHECK_FOR_HANG_TIME_IN_SECONDS;
        AdapterRegistration.InterfaceType = NIC_INTERFACE_TYPE;

        NDIS_DECLARE_MINIPORT_ADAPTER_CONTEXT(MP_ADAPTER);
        Status = NdisMSetMiniportAttributes(
                MiniportAdapterHandle,
                (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&AdapterRegistration);
        if (NDIS_STATUS_SUCCESS != Status)
        {
            DEBUGP(MP_ERROR, "[%p] NdisSetOptionalHandlers Status 0x%08x\n", Adapter, Status);
            break;
        }

        //
        // Read Advanced configuration information from the registry
        //
        Status = NICReadRegParameters(Adapter);
        if(Status != NDIS_STATUS_SUCCESS)
        {
            break;
        }

        //
        // NdisMGetDeviceProperty function enables us to get the:
        // PDO - created by the bus driver to represent our device.
        // FDO - created by NDIS to represent our miniport as a function driver.
        // NextDeviceObject - deviceobject of another driver (filter)
        //                      attached to us at the bottom.
        // In a pure NDIS miniport driver, there is no use for this
        // information, but a NDISWDM driver would need to know this so that it
        // can transfer packets to the lower WDM stack using IRPs.
        //
        NdisMGetDeviceProperty(
                MiniportAdapterHandle,
                &Adapter->Pdo,
                &Adapter->Fdo,
                &Adapter->NextDeviceObject,
                NULL,
                NULL);

        //
        // Allocate default DPC, using current processor information (this will be
        // added to the DPC list by the allocation function). The default DPC
        // is used for all receives when VMQ is not enabled, and for the default queue
        // when VMQ is enabled.
        //
        Adapter->DefaultRecvDpc = NICAllocReceiveDpc(Adapter, 0, 0, 0);
        if(!Adapter->DefaultRecvDpc)
        {
            DEBUGP(MP_ERROR, "[%p] Failed while allocating the default DPC\n", Adapter);
            Status = NDIS_STATUS_RESOURCES;
            break;
        }

        //
        // If VMQ is enabled, allocate the default receive queue. Otherwise, initialize the default receive
        // block for use in non-VMQ receives
        //
        if(VMQ_ENABLED(Adapter))
        {
            Status = AllocateDefaultRxQueue(Adapter);
            if(Status != NDIS_STATUS_SUCCESS)
            {
                DEBUGP(MP_ERROR, "[%p] AllocateDefaultRxQueue Status 0x%08x\n", Adapter, Status);
                break;
            }
        }
        else
        {
            //
            // Initialize the default receive block
            //
            Status = NICInitializeReceiveBlock(Adapter, 0);
            if(Status != NDIS_STATUS_SUCCESS)
            {
                DEBUGP(MP_ERROR, "[%p] NICInitializeReceiveBlock Status 0x%08x\n", Adapter, Status);
                break;
            }

            //
            // Initialize list and lock of the free Rcb list
            //
            NdisInitializeListHead(&Adapter->FreeRcbList);
            NdisAllocateSpinLock(&Adapter->FreeRcbListLock);

            //
            // Allocate the adapter's non-VMQ RCB & receive NBL data
            //
            Status = NICAllocRCBData(
                Adapter,
                NIC_MAX_BUSY_RECVS,
                &Adapter->RcbMemoryBlock,
                &Adapter->FreeRcbList,
                &Adapter->FreeRcbListLock,
                &Adapter->RecvNblPoolHandle
                );
            if(Status != NDIS_STATUS_SUCCESS)
            {
                break;
            }
        }

        //
        // Next, set the general attributes.
        //

#if (NDIS_SUPPORT_NDIS620)
        {C_ASSERT(sizeof(AdapterGeneral) >= NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2);}
        AdapterGeneral.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES;
        AdapterGeneral.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2;
        AdapterGeneral.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2;
#elif (NDIS_SUPPORT_NDIS6)
        {C_ASSERT(sizeof(AdapterGeneral) >= NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1);}
        AdapterGeneral.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES;
        AdapterGeneral.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1;
        AdapterGeneral.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1;
#endif // NDIS MINIPORT VERSION

        //
        // Specify the medium type that the NIC can support but not
        // necessarily the medium type that the NIC currently uses.
        //
        AdapterGeneral.MediaType = NIC_MEDIUM_TYPE;

        //
        // Specifiy medium type that the NIC currently uses.
        //
        AdapterGeneral.PhysicalMediumType = NIC_PHYSICAL_MEDIUM;

        //
        // Specifiy the maximum network frame size, in bytes, that the NIC
        // supports excluding the header. A NIC driver that emulates another
        // medium type for binding to a transport must ensure that the maximum
        // frame size for a protocol-supplied net buffer does not exceed the
        // size limitations for the true network medium.
        //
        AdapterGeneral.MtuSize = HW_FRAME_MAX_DATA_SIZE;
        AdapterGeneral.MaxXmitLinkSpeed = Adapter->ulLinkSendSpeed;
        AdapterGeneral.XmitLinkSpeed = Adapter->ulLinkSendSpeed;
        AdapterGeneral.MaxRcvLinkSpeed = Adapter->ulLinkRecvSpeed;
        AdapterGeneral.RcvLinkSpeed = Adapter->ulLinkRecvSpeed;
        AdapterGeneral.MediaConnectState = HWGetMediaConnectStatus(Adapter);
        AdapterGeneral.MediaDuplexState = MediaDuplexStateFull;

        //
        // The maximum number of bytes the NIC can provide as lookahead data.
        // If that value is different from the size of the lookahead buffer
        // supported by bound protocols, NDIS will call MiniportOidRequest to
        // set the size of the lookahead buffer provided by the miniport driver
        // to the minimum of the miniport driver and protocol(s) values. If the
        // driver always indicates up full packets with
        // NdisMIndicateReceiveNetBufferLists, it should set this value to the
        // maximum total frame size, which excludes the header.
        //
        // Upper-layer drivers examine lookahead data to determine whether a
        // packet that is associated with the lookahead data is intended for
        // one or more of their clients. If the underlying driver supports
        // multipacket receive indications, bound protocols are given full net
        // packets on every indication. Consequently, this value is identical
        // to that returned for OID_GEN_RECEIVE_BLOCK_SIZE.
        //
        AdapterGeneral.LookaheadSize = Adapter->ulLookahead;
        AdapterGeneral.MacOptions = NIC_MAC_OPTIONS;
        AdapterGeneral.SupportedPacketFilters = NIC_SUPPORTED_FILTERS;


        //
        // The maximum number of multicast addresses the NIC driver can manage.
        // This list is global for all protocols bound to (or above) the NIC.
        // Consequently, a protocol can receive NDIS_STATUS_MULTICAST_FULL from
        // the NIC driver when attempting to set the multicast address list,
        // even if the number of elements in the given list is less than the
        // number originally returned for this query.
        //
        AdapterGeneral.MaxMulticastListSize = NIC_MAX_MCAST_LIST;
        AdapterGeneral.MacAddressLength = NIC_MACADDR_SIZE;


        //
        // Return the MAC address of the NIC burnt in the hardware.
        //
        NIC_COPY_ADDRESS(AdapterGeneral.PermanentMacAddress, Adapter->PermanentAddress);

        //
        // Return the MAC address the NIC is currently programmed to use. Note
        // that this address could be different from the permananent address as
        // the user can override using registry. Read NdisReadNetworkAddress
        // doc for more info.
        //
        NIC_COPY_ADDRESS(AdapterGeneral.CurrentMacAddress, Adapter->CurrentAddress);
        AdapterGeneral.RecvScaleCapabilities = NULL;
        AdapterGeneral.AccessType = NIC_ACCESS_TYPE;
        AdapterGeneral.DirectionType = NIC_DIRECTION_TYPE;
        AdapterGeneral.ConnectionType = NIC_CONNECTION_TYPE;
        AdapterGeneral.IfType = NIC_IFTYPE;
        AdapterGeneral.IfConnectorPresent = NIC_HAS_PHYSICAL_CONNECTOR;
        AdapterGeneral.SupportedStatistics = NIC_SUPPORTED_STATISTICS;
        AdapterGeneral.SupportedPauseFunctions = NdisPauseFunctionsUnsupported;
        AdapterGeneral.DataBackFillSize = 0;
        AdapterGeneral.ContextBackFillSize = 0;


        //
        // The SupportedOidList is an array of OIDs for objects that the
        // underlying driver or its NIC supports.  Objects include general,
        // media-specific, and implementation-specific objects. NDIS forwards a
        // subset of the returned list to protocols that make this query. That
        // is, NDIS filters any supported statistics OIDs out of the list
        // because protocols never make statistics queries.
        //
        AdapterGeneral.SupportedOidList = NICSupportedOids;
        AdapterGeneral.SupportedOidListLength = sizeof(NICSupportedOids);
        AdapterGeneral.AutoNegotiationFlags = NDIS_LINK_STATE_DUPLEX_AUTO_NEGOTIATED;

        //
        // Set the power management capabilities.  The format used is NDIS
        // version-specific.
        //
#if (NDIS_SUPPORT_NDIS620)

        NdisZeroMemory(&PmCapabilities, sizeof(PmCapabilities));

        {C_ASSERT(sizeof(PmCapabilities) >= NDIS_SIZEOF_NDIS_PM_CAPABILITIES_REVISION_1);}
        PmCapabilities.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
        PmCapabilities.Header.Size = NDIS_SIZEOF_NDIS_PM_CAPABILITIES_REVISION_1;
        PmCapabilities.Header.Revision = NDIS_PM_CAPABILITIES_REVISION_1;

        PmCapabilities.MinMagicPacketWakeUp = NIC_MAGIC_PACKET_WAKEUP;
        PmCapabilities.MinPatternWakeUp = NIC_PATTERN_WAKEUP;
        PmCapabilities.MinLinkChangeWakeUp = NIC_LINK_CHANGE_WAKEUP;

        AdapterGeneral.PowerManagementCapabilitiesEx = &PmCapabilities;

#elif (NDIS_SUPPORT_NDIS6)

        NdisZeroMemory(&PnpCapabilities, sizeof(PnpCapabilities));
        PnpCapabilities.WakeUpCapabilities.MinMagicPacketWakeUp = NIC_MAGIC_PACKET_WAKEUP;
        PnpCapabilities.WakeUpCapabilities.MinPatternWakeUp = NIC_PATTERN_WAKEUP;
        AdapterGeneral.PowerManagementCapabilities = &PnpCapabilities; // Optional

#endif // NDIS MINIPORT VERSION

        Status = NdisMSetMiniportAttributes(
                MiniportAdapterHandle,
                (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&AdapterGeneral);
        if (NDIS_STATUS_SUCCESS != Status)
        {
            DEBUGP(MP_ERROR, "[%p] NdisSetOptionalHandlers Status 0x%08x\n", Adapter, Status);
            break;
        }

        //
        // Set Miniport attributes for supported and enabled VMQ features.
        //
        Status = InitializeRxQueueMPConfig(Adapter);
        if (NDIS_STATUS_SUCCESS != Status)
        {
            DEBUGP(MP_ERROR, "[%p] InitializeRxQueueMPConfig Status 0x%08x\n", Adapter, Status);
            break;
        }

        //
        // Set miniport attributes for supported and enabled NDIS QOS features.
        //
        Status = InitializeQOSConfig(Adapter);
        if (NDIS_STATUS_SUCCESS != Status)
        {
            DEBUGP(MP_ERROR, "[%p] InitializeQOSConfig Status 0x%08x\n", Adapter, Status);
            break;
        }

        //
        // For hardware devices, you should register your interrupt handlers
        // here, using NdisMRegisterInterruptEx.
        //

        //
        // Get the Adapter Resources & Initialize the hardware.
        //

        Status = HWInitialize(Adapter, MiniportInitParameters);
        if(Status != NDIS_STATUS_SUCCESS)
        {
            Status = NDIS_STATUS_FAILURE;
            break;
        }
    } while(FALSE);


    if (Status != NDIS_STATUS_SUCCESS)
    {
        if (Adapter)
        {
            NICFreeAdapter(Adapter);
        }
        Adapter = NULL;
    }


    DEBUGP(MP_TRACE, "[%p] <--- MPInitializeEx Status = 0x%08x%\n", Adapter, Status);
    return Status;
}


NDIS_STATUS
MPPause(
    _In_  NDIS_HANDLE  MiniportAdapterContext,
    _In_  PNDIS_MINIPORT_PAUSE_PARAMETERS MiniportPauseParameters)
/*++

Routine Description:

    When a miniport receives a pause request, it enters into a Pausing state.
    The miniport should not indicate up any more network data.  Any pending
    send requests must be completed, and new requests must be rejected with
    NDIS_STATUS_PAUSED.

    Once all sends have been completed and all recieve NBLs have returned to
    the miniport, the miniport enters the Paused state.

    While paused, the miniport can still service interrupts from the hardware
    (to, for example, continue to indicate NDIS_STATUS_MEDIA_CONNECT
    notifications).

    The miniport must continue to be able to handle status indications and OID
    requests.  MiniportPause is different from MiniportHalt because, in
    general, the MiniportPause operation won't release any resources.
    MiniportPause must not attempt to acquire any resources where allocation
    can fail, since MiniportPause itself must not fail.


    MiniportPause runs at IRQL = PASSIVE_LEVEL.

Arguments:

    MiniportAdapterContext  Pointer to the Adapter
    MiniportPauseParameters  Additional information about the pause operation

Return Value:

    If the miniport is able to immediately enter the Paused state, it should
    return NDIS_STATUS_SUCCESS.

    If the miniport must wait for send completions or pending receive NBLs, it
    should return NDIS_STATUS_PENDING now, and call NDISMPauseComplete when the
    miniport has entered the Paused state.

    No other return value is permitted.  The pause operation must not fail.

--*/
{
    NDIS_STATUS Status;

    PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext);

    DEBUGP(MP_TRACE, "[%p] ---> MPPause\n", Adapter);
    UNREFERENCED_PARAMETER(MiniportPauseParameters);
    PAGED_CODE();

    MP_SET_FLAG(Adapter, fMP_ADAPTER_PAUSE_IN_PROGRESS);

    NICStopTheDatapath(Adapter);


    if (NICIsBusy(Adapter))
    {
        //
        // The adapter is busy sending or receiving data, so the pause must
        // be completed asynchronously later.
        //
        NICScheduleTheResetOrPauseDpc(Adapter, FALSE);
        Status = NDIS_STATUS_PENDING;
    }
    else // !NICIsBusy
    {
        //
        // The pause operation has completed synchronously.
        //
        MP_SET_FLAG(Adapter, fMP_ADAPTER_PAUSED);
        MP_CLEAR_FLAG(Adapter, fMP_ADAPTER_PAUSE_IN_PROGRESS);
        Status = NDIS_STATUS_SUCCESS;
    }

    DEBUGP(MP_TRACE, "[%p] <--- MPPause\n", Adapter);
    return Status;
}


NDIS_STATUS
MPRestart(
    _In_  NDIS_HANDLE                             MiniportAdapterContext,
    _In_  PNDIS_MINIPORT_RESTART_PARAMETERS       RestartParameters)
/*++

Routine Description:

    When a miniport receives a restart request, it enters into a Restarting
    state.  The miniport may begin indicating received data (e.g., using
    NdisMIndicateReceiveNetBufferLists), handling status indications, and
    processing OID requests in the Restarting state.  However, no sends will be
    requested while the miniport is in the Restarting state.

    Once the miniport is ready to send data, it has entered the Running state.
    The miniport informs NDIS that it is in the Running state by returning
    NDIS_STATUS_SUCCESS from this MiniportRestart function; or if this function
    has already returned NDIS_STATUS_PENDING, by calling NdisMRestartComplete.


    MiniportRestart runs at IRQL = PASSIVE_LEVEL.

Arguments:

    MiniportAdapterContext  Pointer to the Adapter
    RestartParameters  Additional information about the restart operation

Return Value:

    If the miniport is able to immediately enter the Running state, it should
    return NDIS_STATUS_SUCCESS.

    If the miniport is still in the Restarting state, it should return
    NDIS_STATUS_PENDING now, and call NdisMRestartComplete when the miniport
    has entered the Running state.

    Other NDIS_STATUS codes indicate errors.  If an error is encountered, the
    miniport must return to the Paused state (i.e., stop indicating receives).

--*/

{
    NDIS_STATUS Status = NDIS_STATUS_PENDING;

    PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext);

    DEBUGP(MP_TRACE, "[%p] ---> MPRestart\n", Adapter);
    UNREFERENCED_PARAMETER(Adapter);
    UNREFERENCED_PARAMETER(RestartParameters);
    PAGED_CODE();


    MP_CLEAR_FLAG(Adapter, (fMP_ADAPTER_PAUSE_IN_PROGRESS|fMP_ADAPTER_PAUSED));

    NICStartTheDatapath(Adapter);


    //
    // The simulated hardware is immediately ready to send data again, so in
    // this sample code, MiniportRestart always returns success.  If we had to
    // wait for hardware to reinitialize, we'd return NDIS_STATUS_PENDING now,
    // and call NdisMRestartComplete later.
    //
    Status = NDIS_STATUS_SUCCESS;


    DEBUGP(MP_TRACE, "[%p] <--- MPRestart\n", Adapter);
    return Status;
}


VOID
MPHaltEx(
    IN  NDIS_HANDLE MiniportAdapterContext,
    IN  NDIS_HALT_ACTION HaltAction
    )
/*++

Routine Description:

    Halt handler is called when NDIS receives IRP_MN_STOP_DEVICE,
    IRP_MN_SUPRISE_REMOVE or IRP_MN_REMOVE_DEVICE requests from the PNP
    manager. Here, the driver should free all the resources acquired in
    MiniportInitialize and stop access to the hardware. NDIS will not submit
    any further request once this handler is invoked.

    1) Free and unmap all I/O resources.
    2) Disable interrupt and deregister interrupt handler.
    3) Deregister shutdown handler regsitered by
        NdisMRegisterAdapterShutdownHandler .
    4) Cancel all queued up timer callbacks.
    5) Finally wait indefinitely for all the outstanding receive
        packets indicated to the protocol to return.

    MiniportHalt runs at IRQL = PASSIVE_LEVEL.


Arguments:

    MiniportAdapterContext  Pointer to the Adapter
    HaltAction  The reason for halting the adapter

Return Value:

    None.

--*/
{
    PMP_ADAPTER       Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext);
    LONG              nHaltCount = 0;


    PAGED_CODE();
    DEBUGP(MP_TRACE, "[%p] ---> MPHaltEx\n", Adapter);
    UNREFERENCED_PARAMETER(HaltAction);

    MP_SET_FLAG(Adapter, fMP_ADAPTER_HALT_IN_PROGRESS);

    //
    // Call Shutdown handler to disable interrupt and turn the hardware off by
    // issuing a full reset
    //
    // On XP and later, NDIS notifies our PNP event handler the reason for
    // calling Halt. So before accessing the device, check to see if the device
    // is surprise removed, if so don't bother calling the shutdown handler to
    // stop the hardware because it doesn't exist.
    //
    if(!MP_TEST_FLAG(Adapter, fMP_ADAPTER_SURPRISE_REMOVED)) {
        MPShutdownEx(MiniportAdapterContext, NdisShutdownPowerOff);
    }


    NICStopTheDatapath(Adapter);


    while (NICIsBusy(Adapter) ||
           Adapter->SendCompleteWorkItemQueued ||
           Adapter->SendCompleteWorkItemRunning)
    {
        if (++nHaltCount % 100 == 0)
        {
            DEBUGP(MP_ERROR, "[%p] Halt timed out!\n", Adapter);
            ASSERT(FALSE);
        }

        DEBUGP(MP_INFO, "[%p] MPHaltEx - waiting ...\n", Adapter);
        NdisMSleep(1000);
    }


    NICFreeAdapter(Adapter);

    DEBUGP(MP_TRACE, "[%p] <--- MPHaltEx\n", Adapter);
}


NDIS_STATUS
MPResetEx(
    _In_  NDIS_HANDLE MiniportAdapterContext,
    _Out_ PBOOLEAN AddressingReset)
/*++

Routine Description:

    MiniportResetEx is a required to issue a hardware reset to the NIC
    and/or to reset the driver's software state.

    1) The miniport driver can optionally complete any pending
        OID requests. NDIS will submit no further OID requests
        to the miniport driver for the NIC being reset until
        the reset operation has finished. After the reset,
        NDIS will resubmit to the miniport driver any OID requests
        that were pending but not completed by the miniport driver
        before the reset.

    2) A deserialized miniport driver must complete any pending send
        operations. NDIS will not requeue pending send packets for
        a deserialized driver since NDIS does not maintain the send
        queue for such a driver.

    3) If MiniportReset returns NDIS_STATUS_PENDING, the driver must
        complete the original request subsequently with a call to
        NdisMResetComplete.

    MiniportReset runs at IRQL <= DISPATCH_LEVEL.

Arguments:

AddressingReset - If multicast or functional addressing information
                  or the lookahead size, is changed by a reset,
                  MiniportReset must set the variable at AddressingReset
                  to TRUE before it returns control. This causes NDIS to
                  call the MiniportSetInformation function to restore
                  the information.

MiniportAdapterContext - Pointer to our adapter

Return Value:

    NDIS_STATUS

--*/
{
    NDIS_STATUS       Status;
    PMP_ADAPTER       Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext);

    DEBUGP(MP_TRACE, "[%p] ---> MPResetEx\n", Adapter);

    *AddressingReset = FALSE;

    do
    {
        ASSERT(!MP_TEST_FLAG(Adapter, fMP_ADAPTER_HALT_IN_PROGRESS));

        if (MP_TEST_FLAG(Adapter, fMP_RESET_IN_PROGRESS))
        {
            Status = NDIS_STATUS_RESET_IN_PROGRESS;
            break;
        }

        MP_SET_FLAG(Adapter, fMP_RESET_IN_PROGRESS);

        //
        // Complete all the queued up send packets
        //
        TXFlushSendQueue(Adapter, NDIS_STATUS_RESET_IN_PROGRESS);

        if (NICIsBusy(Adapter))
        {
            NICScheduleTheResetOrPauseDpc(Adapter, FALSE);

            //
            // By returning NDIS_STATUS_PENDING, we are promising NDIS that
            // we will complete the reset request by calling NdisMResetComplete.
            //
            Status = NDIS_STATUS_PENDING;
            break;
        }

        MP_CLEAR_FLAG(Adapter, fMP_RESET_IN_PROGRESS);

        Status = NDIS_STATUS_SUCCESS;

    } while(FALSE);


    DEBUGP(MP_TRACE, "[%p] <--- MPResetEx Status = 0x%08x\n", Adapter, Status);
    return Status;
}


VOID
NICScheduleTheResetOrPauseDpc(
    _In_  PMP_ADAPTER  Adapter,
    _In_  BOOLEAN      fReschedule)
/*++

Routine Description:

    Schedules the timer callback function for asynchronous pause/reset.

Arguments:

    Adapter      -
    fReschedule  - TRUE if this is rescheduling an existing async pause/reset
                   operation, or FALSE if this is a new pause/reset operation.

Return Value:

    None.

--*/
{
    LARGE_INTEGER liRetryTime;

    DEBUGP(MP_TRACE, "[%p] ---> NICScheduleTheResetOrPauseDpc\n", Adapter);

    if (!fReschedule)
    {
        Adapter->AsyncBusyCheckCount = 0;
    }


    liRetryTime.QuadPart = -1000000LL; // 100ms in 100ns increments
    NdisSetTimerObject(Adapter->AsyncBusyCheckTimer, liRetryTime, 0, NULL);

    DEBUGP(MP_TRACE, "[%p] <--- NICScheduleTheResetOrPauseDpc\n", Adapter);
}


_Use_decl_annotations_
VOID
NICAsyncResetOrPauseDpc(
    PVOID                    SystemSpecific1,
    PVOID                    FunctionContext,
    PVOID                    SystemSpecific2,
    PVOID                    SystemSpecific3)
/*++

Routine Description:

    Timer callback function for Reset operation.

Arguments:

FunctionContext - Pointer to our adapter

Return Value:

    None.

--*/
{
    PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(FunctionContext);
    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;

    UNREFERENCED_PARAMETER(SystemSpecific1);
    UNREFERENCED_PARAMETER(SystemSpecific2);
    UNREFERENCED_PARAMETER(SystemSpecific3);


    DEBUGP(MP_TRACE, "[%p] ---> NICAsyncResetOrPauseDpc\n", Adapter);

    if (MP_TEST_FLAG(Adapter, fMP_ADAPTER_PAUSE_IN_PROGRESS)
            || MP_TEST_FLAG(Adapter, fMP_RESET_IN_PROGRESS))
    {
        BOOLEAN fBusy = NICIsBusy(Adapter);

        if (fBusy && ++Adapter->AsyncBusyCheckCount <= 20)
        {
            //
            // Still busy -- let's try another time.
            //
            NICScheduleTheResetOrPauseDpc(Adapter, TRUE);
        }
        else
        {
            if (fBusy)
            {

                //
                // We have tried enough. Something is wrong. Let us
                // just complete the reset request with failure.
                //
                DEBUGP(MP_ERROR, "[%p] Reset timed out!!!\n", Adapter);

                ASSERT(FALSE);

                Status = NDIS_STATUS_FAILURE;
            }


            if (MP_TEST_FLAG(Adapter, fMP_ADAPTER_PAUSE_IN_PROGRESS))
            {
                DEBUGP(MP_INFO, "[%p] Done - NdisMPauseComplete\n", Adapter);

                MP_SET_FLAG(Adapter, fMP_ADAPTER_PAUSED);
                MP_CLEAR_FLAG(Adapter, fMP_ADAPTER_PAUSE_IN_PROGRESS);

                NdisMPauseComplete(Adapter->AdapterHandle);
            }
            else if (MP_TEST_FLAG(Adapter, fMP_RESET_IN_PROGRESS))
            {
                DEBUGP(MP_INFO, "[%p] Done - NdisMResetComplete\n", Adapter);

                MP_CLEAR_FLAG(Adapter, fMP_RESET_IN_PROGRESS);

                NdisMResetComplete(Adapter->AdapterHandle, Status, FALSE);
            }
        }
    }


    DEBUGP(MP_TRACE, "[%p] <--- NICAsyncResetOrPauseDpc Status = 0x%08x\n", Adapter, Status);
}

VOID
MPShutdownEx(
    _In_  NDIS_HANDLE           MiniportAdapterContext,
    _In_  NDIS_SHUTDOWN_ACTION  ShutdownAction)
/*++

Routine Description:

    The MiniportShutdownEx handler restores hardware to its initial state when
    the system is shut down, whether by the user or because an unrecoverable
    system error occurred. This is to ensure that the NIC is in a known
    state and ready to be reinitialized when the machine is rebooted after
    a system shutdown occurs for any reason, including a crash dump.

    Here just disable the interrupt and stop the DMA engine.  Do not free
    memory resources or wait for any packet transfers to complete.  Do not call
    into NDIS at this time.

    This can be called at aribitrary IRQL, including in the context of a
    bugcheck.

Arguments:

    MiniportAdapterContext  Pointer to our adapter
    ShutdownAction  The reason why NDIS called the shutdown function

Return Value:

    None.

--*/
{
    PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext);
    UNREFERENCED_PARAMETER(ShutdownAction);
    UNREFERENCED_PARAMETER(Adapter);

    DEBUGP(MP_TRACE, "[%p] ---> MPShutdownEx\n", Adapter);

    //
    // We don't have any hardware to reset.
    //

    DEBUGP(MP_TRACE, "[%p] <--- MPShutdownEx\n", Adapter);
}


BOOLEAN
MPCheckForHangEx(
    _In_  NDIS_HANDLE MiniportAdapterContext)
/*++

Routine Description:

    The MiniportCheckForHangEx handler is called to report the state of the
    NIC, or to monitor the responsiveness of an underlying device driver.
    This is an optional function. If this handler is not specified, NDIS
    judges the driver unresponsive when the driver holds
    MiniportQueryInformation or MiniportSetInformation requests for a
    time-out interval (deafult 4 sec), and then calls the driver's
    MiniportReset function. A NIC driver's MiniportInitialize function can
    extend NDIS's time-out interval by calling NdisMSetAttributesEx to
    avoid unnecessary resets.

    MiniportCheckForHangEx runs at IRQL <= DISPATCH_LEVEL.

Arguments:

    MiniportAdapterContext  Pointer to our adapter

Return Value:

    TRUE    NDIS calls the driver's MiniportReset function.
    FALSE   Everything is fine

--*/
{
    PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext);

    DEBUGP(MP_TRACE, "[%p] ---> MPCheckForHangEx\n", Adapter);
    DEBUGP(MP_TRACE, "[%p] <--- MPCheckForHangEx. FALSE\n", Adapter);
    return FALSE;
}


VOID
MPDevicePnpEventNotify(
    _In_  NDIS_HANDLE             MiniportAdapterContext,
    _In_  PNET_DEVICE_PNP_EVENT   NetDevicePnPEvent)
/*++

Routine Description:



    Runs at IRQL = PASSIVE_LEVEL in the context of system thread.

Arguments:

    MiniportAdapterContext      Pointer to our adapter
    NetDevicePnPEvent           Self-explanatory

Return Value:

    None.

--*/
{
    PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext);

    DEBUGP(MP_TRACE, "[%p] ---> MPDevicePnpEventNotify\n", Adapter);

    PAGED_CODE();


    switch (NetDevicePnPEvent->DevicePnPEvent)
    {
        case NdisDevicePnPEventSurpriseRemoved:
            //
            // Called when NDIS receives IRP_MN_SUPRISE_REMOVAL.
            // NDIS calls MiniportHalt function after this call returns.
            //
            MP_SET_FLAG(Adapter, fMP_ADAPTER_SURPRISE_REMOVED);
            DEBUGP(MP_INFO, "[%p] MPDevicePnpEventNotify: NdisDevicePnPEventSurpriseRemoved\n", Adapter);
            break;

        case NdisDevicePnPEventPowerProfileChanged:
            //
            // After initializing a miniport driver and after miniport driver
            // receives an OID_PNP_SET_POWER notification that specifies
            // a device power state of NdisDeviceStateD0 (the powered-on state),
            // NDIS calls the miniport's MiniportPnPEventNotify function with
            // PnPEvent set to NdisDevicePnPEventPowerProfileChanged.
            //
            DEBUGP(MP_INFO, "[%p] MPDevicePnpEventNotify: NdisDevicePnPEventPowerProfileChanged\n", Adapter);

            if (NetDevicePnPEvent->InformationBufferLength == sizeof(ULONG))
            {
                ULONG NdisPowerProfile = *((PULONG)NetDevicePnPEvent->InformationBuffer);

                if (NdisPowerProfile == NdisPowerProfileBattery)
                {
                    DEBUGP(MP_INFO, "[%p] The host system is running on battery power\n", Adapter);
                }
                if (NdisPowerProfile == NdisPowerProfileAcOnLine)
                {
                    DEBUGP(MP_INFO, "[%p] The host system is running on AC power\n", Adapter);
                }
            }
            break;

        default:
            DEBUGP(MP_ERROR, "[%p] MPDevicePnpEventNotify: unknown PnP event 0x%x\n", Adapter, NetDevicePnPEvent->DevicePnPEvent);
    }


    DEBUGP(MP_TRACE, "[%p] <--- MPDevicePnpEventNotify\n", Adapter);
}

NDIS_STATUS
NICAllocAdapter(
    _In_ NDIS_HANDLE MiniportAdapterHandle,
    _Outptr_ PMP_ADAPTER *pAdapter)
/*++
Routine Description:

    The NICAllocAdapter function allocates and initializes the memory used to track a miniport instance.

    NICAllocAdapter runs at IRQL = PASSIVE_LEVEL.

Arguments:

    MiniportAdapterHandle       NDIS handle for the adapter.
    pAdapter                    Receives the allocated and initialized adapter memory.

    Return Value:

    NDIS_STATUS_xxx code

--*/
{
    PMP_ADAPTER Adapter = NULL;
    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
    LONG index;

    DEBUGP(MP_TRACE, "---> NICAllocAdapter\n");

    PAGED_CODE();

    *pAdapter = NULL;

    do
    {
        //
        // Allocate extra space for the MP_ADAPTER memory (one cache line's worth) so that we can
        // reference the memory from a cache-aligned starting point. This way we guarantee that
        // members with cache-aligned directives are actually cache aligned.
        //
        PVOID UnalignedAdapterBuffer = NULL;
        ULONG UnalignedAdapterBufferSize = sizeof(MP_ADAPTER)+ NdisGetSharedDataAlignment();
        NDIS_TIMER_CHARACTERISTICS Timer;

        //
        // Allocate memory for adapter context (unaligned)
        //
        UnalignedAdapterBuffer = NdisAllocateMemoryWithTagPriority(
                NdisDriverHandle,
                UnalignedAdapterBufferSize,
                NIC_TAG,
                NormalPoolPriority);
        if (!UnalignedAdapterBuffer)
        {
            Status = NDIS_STATUS_RESOURCES;
            DEBUGP(MP_ERROR, "Failed to allocate memory for adapter context\n");
            break;
        }

        //
        // Zero the memory block
        //
        NdisZeroMemory(UnalignedAdapterBuffer, UnalignedAdapterBufferSize);

        //
        // Start the Adapter pointer at a cache-aligned boundary
        //
        Adapter = ALIGN_UP_POINTER_BY(UnalignedAdapterBuffer, NdisGetSharedDataAlignment());

        //
        // Store the unaligned information so that we can free it later
        //
        Adapter->UnalignedAdapterBuffer = UnalignedAdapterBuffer;
        Adapter->UnalignedAdapterBufferSize = UnalignedAdapterBufferSize;

        //
        // Set the adapter handle
        //
        Adapter->AdapterHandle = MiniportAdapterHandle;

        NdisInitializeListHead(&Adapter->List);

        //
        // Initialize Send & Recv listheads and corresponding
        // spinlocks.
        //
        NdisInitializeListHead(&Adapter->FreeTcbList);
        NdisAllocateSpinLock(&Adapter->FreeTcbListLock);

        NdisInitializeListHead(&Adapter->SendWaitList);
        NdisAllocateSpinLock(&Adapter->SendWaitListLock);

        NdisInitializeListHead(&Adapter->BusyTcbList);
        NdisAllocateSpinLock(&Adapter->BusyTcbListLock);

        KeInitializeSpinLock(&Adapter->SendPathSpinLock);

        //
        // Set the default lookahead buffer size.
        //
        Adapter->ulLookahead = NIC_MAX_LOOKAHEAD;

        //
        // Allocate data for Send and Receive Control blocks.
        //

        Adapter->TcbMemoryBlock = NdisAllocateMemoryWithTagPriority(
                MiniportAdapterHandle,
                sizeof(TCB) * NIC_MAX_BUSY_SENDS,
                NIC_TAG_TCB,
                NormalPoolPriority);

        if (!Adapter->TcbMemoryBlock)
        {
            Status = NDIS_STATUS_RESOURCES;
            DEBUGP(MP_ERROR, "[%p] NdisAllocateMemoryWithTagPriority failed\n", Adapter);
            break;
        }

        for (index = 0; index < NIC_MAX_BUSY_SENDS; index++)
        {
            NdisInterlockedInsertTailList(
                    &Adapter->FreeTcbList,
                    &((PTCB)Adapter->TcbMemoryBlock)[index].TcbLink,
                    &Adapter->FreeTcbListLock);
        }

        //
        // Set up timers to simulate hardware interrupts (SendComplete and Recv)
        //

        NdisZeroMemory(&Timer, sizeof(Timer));

        {C_ASSERT(NDIS_SIZEOF_TIMER_CHARACTERISTICS_REVISION_1 <= sizeof(Timer));}
        Timer.Header.Type = NDIS_OBJECT_TYPE_TIMER_CHARACTERISTICS;
        Timer.Header.Size = NDIS_SIZEOF_TIMER_CHARACTERISTICS_REVISION_1;
        Timer.Header.Revision = NDIS_TIMER_CHARACTERISTICS_REVISION_1;

        Timer.TimerFunction = TXSendCompleteDpc;
        Timer.FunctionContext = Adapter;
        Timer.AllocationTag = NIC_TAG_TIMER;

        Status = NdisAllocateTimerObject(
                NdisDriverHandle,
                &Timer,
                &Adapter->SendCompleteTimer);
        if (Status != NDIS_STATUS_SUCCESS)
        {
            Status = NDIS_STATUS_FAILURE;
            break;
        }

        Adapter->SendCompleteWorkItem = NdisAllocateIoWorkItem(MiniportAdapterHandle);
        if (Adapter->SendCompleteWorkItem == NULL)
        {
            Status = NDIS_STATUS_RESOURCES;
            break;
        }

        //
        // Initialize DPC list data
        //
        NdisInitializeListHead(&Adapter->RecvDpcList);
        NdisAllocateSpinLock(&Adapter->RecvDpcListLock);

        //
        // Set up a timer function for use with our MPReset routine.
        //
        Timer.TimerFunction = NICAsyncResetOrPauseDpc;

        Status = NdisAllocateTimerObject(
                NdisDriverHandle,
                &Timer,
                &Adapter->AsyncBusyCheckTimer);
        if (Status != NDIS_STATUS_SUCCESS)
        {
            Status = NDIS_STATUS_FAILURE;
            break;
        }

        //
        // The miniport adapter is powered up
        //
        Adapter->CurrentPowerState = NdisDeviceStateD0;

        //
        // Initialize and allocate the basic VMQ data for this adapter if supported. Queues, filters, shared memory,
        // and other data is allocated at a later time, when the relavant OID's are called to eable/configure them.
        //
        Status = AllocateVMQData(Adapter);
        if (Status != NDIS_STATUS_SUCCESS)
        {
            DEBUGP(MP_ERROR, "[%p] AllocateVMQData Status 0x%08x\n", Adapter, Status);
            Status = NDIS_STATUS_FAILURE;
            break;
        }

    } while(FALSE);


    *pAdapter = Adapter;

    //
    // In the failure case, the caller of this routine will end up
    // calling NICFreeAdapter to free all the successfully allocated
    // resources.
    //
    DEBUGP(MP_TRACE, "[%p] <--- NICAllocAdapter\n", Adapter);
    return Status;
}


void NICFreeAdapter(
    _In_  PMP_ADAPTER Adapter)
/*++
Routine Description:

    The NICFreeAdapter function frees memory used to track a miniport instance. Should only be called from MPHaltEx.

    NICFreeAdapter runs at IRQL = PASSIVE_LEVEL.

Arguments:

    Adapter                    Adapter memory to free.

    Return Value:

    NDIS_STATUS_xxx code

--*/
{
    PLIST_ENTRY pEntry;

    DEBUGP(MP_TRACE, "[%p] ---> NICFreeAdapter\n", Adapter);

    ASSERT(Adapter);

    //
    // Free all the resources we allocated in NICAllocAdapter.
    //

    if (Adapter->AsyncBusyCheckTimer)
    {
        NdisFreeTimerObject(Adapter->AsyncBusyCheckTimer);
        Adapter->AsyncBusyCheckTimer = NULL;
    }

    if(Adapter->SendCompleteTimer)
    {
        NdisFreeTimerObject(Adapter->SendCompleteTimer);
        Adapter->SendCompleteTimer = NULL;
    }

    if (Adapter->SendCompleteWorkItem)
    {
        ASSERT(!Adapter->SendCompleteWorkItemQueued);
        NdisFreeIoWorkItem(Adapter->SendCompleteWorkItem);
        Adapter->SendCompleteWorkItem = NULL;
    }

    //
    // If VMQ is enabled, then the global adapter RCB list will not have been allocated
    //
    if(!VMQ_ENABLED(Adapter))
    {
        if(Adapter->FreeRcbList.Flink)
        {
            while (NULL != (pEntry = NdisInterlockedRemoveHeadList(
                    &Adapter->FreeRcbList,
                    &Adapter->FreeRcbListLock)))
            {
                PRCB Rcb = CONTAINING_RECORD(pEntry, RCB, RcbLink);
                NdisFreeNetBufferList(Rcb->Nbl);
            }
        }

        if (Adapter->RecvNblPoolHandle)
        {
            NdisFreeNetBufferListPool(Adapter->RecvNblPoolHandle);
            Adapter->RecvNblPoolHandle = NULL;
        }

        if (Adapter->RcbMemoryBlock)
        {
            NdisFreeMemory(
                    Adapter->RcbMemoryBlock,
                    sizeof(RCB)*NIC_MAX_BUSY_RECVS,
                    0);
            Adapter->RcbMemoryBlock = NULL;
        }

        if(Adapter->DefaultRecvDpc)
        {
            //
            // For non VMQ receives, we've referenced the default queue DPC so we should reduce that count
            //
            NICReceiveDpcRemoveOwnership(Adapter->DefaultRecvDpc, 0);
            Adapter->DefaultRecvDpc = NULL;
        }

    }

    if (Adapter->TcbMemoryBlock)
    {
        NdisFreeMemory(
                Adapter->TcbMemoryBlock,
                sizeof(TCB)*NIC_MAX_BUSY_SENDS,
                0);
        Adapter->TcbMemoryBlock = NULL;
    }

    ASSERT(Adapter->SendWaitList.Flink && IsListEmpty(&Adapter->SendWaitList));
    ASSERT(Adapter->BusyTcbList.Flink && IsListEmpty(&Adapter->BusyTcbList));

    NdisFreeSpinLock(&Adapter->FreeTcbListLock);
    NdisFreeSpinLock(&Adapter->SendWaitListLock);
    NdisFreeSpinLock(&Adapter->BusyTcbListLock);
    NdisFreeSpinLock(&Adapter->FreeRcbListLock);

    //
    // Free any remaining VMQ related data
    //
    FreeVMQData(Adapter);

    //
    // Free receive DPCs
    //
    if(Adapter->RecvDpcList.Flink)
    {
        while (!IsListEmpty(&Adapter->RecvDpcList))
        {
            pEntry = RemoveHeadList(&Adapter->RecvDpcList);
            NICFreeReceiveDpc(CONTAINING_RECORD(pEntry, MP_ADAPTER_RECEIVE_DPC, Entry));
        }
    }
    NdisFreeSpinLock(&Adapter->RecvDpcListLock);

    //
    // Finally free the memory for adapter context.
    //
    NdisFreeMemory(Adapter->UnalignedAdapterBuffer, Adapter->UnalignedAdapterBufferSize, 0);

    DEBUGP(MP_TRACE, "[%p] <--- NICFreeAdapter\n", Adapter);
}

VOID
NICUpdateDPCMaxIndicateCount(
        _In_ PMP_ADAPTER_RECEIVE_DPC ReceiveDpc)
/*++
Routine Description:

    The NICUpdateDPCMaxIndicateCount function updates the maximum amount of NBLs to be indicated per block, based
    on the number of owned receive blocks.

Arguments:

    ReceiveDpc                  The receive DPC being modified
    BlockId                     ID of the receive block to add ownership

    Return Value:

        None
--*/
{
    if(ReceiveDpc->RecvBlockCount)
    {
        //
        // Update MaxNblCountPerIndicate. Scale back the amount of NBL indications we're allowed to do per
        // consumed receive block so that we don't sepnd too much time in the DPC as the number of queues grows large.
        //
        ReceiveDpc->MaxNblCountPerIndicate = NIC_MAX_RECVS_PER_DPC/ReceiveDpc->RecvBlockCount;
    }

}

VOID
NICReceiveDpcSetOwnership(
    _In_ PMP_ADAPTER_RECEIVE_DPC ReceiveDpc,
    _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId)
/*++
Routine Description:

    The NICReceiveDpcSetOwnership function updates the Receive DPC passed in so that it consumes the receive queue of the
    passed in receive block ID. This involves updating the boolean array, block count, and updating the maximum
    amount of NBLs to be indicated per block.

Arguments:

    ReceiveDpc                  The receive DPC being modified
    BlockId                     ID of the receive block to add ownership

    Return Value:

    None
--*/
{
    ASSERT(BlockId <  NIC_SUPPORTED_NUM_QUEUES);

    //
    // If the DPC is not already set to consume receives for this QueueId, increase
    // the reference count and set it to be consumed.
    //
    if(!ReceiveDpc->RecvBlock[BlockId])
    {
        InterlockedIncrement(&ReceiveDpc->RecvBlockCount);
        ReceiveDpc->RecvBlock[BlockId]=TRUE;
        NICUpdateDPCMaxIndicateCount(ReceiveDpc);
    }
}

VOID
NICReceiveDpcRemoveOwnership(
    _In_ PMP_ADAPTER_RECEIVE_DPC ReceiveDpc,
    _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId)
/*++
Routine Description:

    The NICReceiveDpcRemoveOwnership function updates the Receive DPC passed in so that it stops consuming the receive
    queue of the passed in receive block ID. This involves updating the boolean array, block count, and updating the maximum
    amount of NBLs to be indicated per block.

Arguments:

    ReceiveDpc                  The receive DPC being modified
    ReceiveBlockId              ID of the receive block to remove ownership

    Return Value:

    None
--*/
{
    ASSERT(BlockId <  NIC_SUPPORTED_NUM_QUEUES);

    //
    // If the DPC is set to consume receives for this QueueId, decrease
    // the reference count and set it to not be consumed.
    //
    if(ReceiveDpc->RecvBlock[BlockId])
    {
        InterlockedDecrement(&ReceiveDpc->RecvBlockCount);
        ASSERT(ReceiveDpc->RecvBlockCount>=0);
        ReceiveDpc->RecvBlock[BlockId]=FALSE;
        NICUpdateDPCMaxIndicateCount(ReceiveDpc);
    }
}

PMP_ADAPTER_RECEIVE_DPC
NICAllocReceiveDpc(
    _In_ PMP_ADAPTER      Adapter,
    ULONG ProcessorNumber,
    USHORT ProcessorGroup,
    _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId)
/*++
Routine Description:

    The NICAllocReceiveDpc function allocates a receive DPC with the specified processor characteristics
    for the target QueueId. If the adapter has already allocated a DPC that matches the processor requirements,
    the existing DPC is reused and given ownership of consuming the QueueId's receives.

    NICAllocReceiveDpc runs at IRQL = PASSIVE_LEVEL.

Arguments:

    Adapter                     Pointer to the adapter that will own the DPC.
    ProcessorNumber             Target processor for DPC
    ProcessorGroup              Target processor group for the DPC (if not Win7, set to 0)
    QueueId                     Queue whose receives the DPC should consume (0 for non-VMQ scenarios)

    Return Value:

    PMP_ADAPTER_RECEIVE_DPC structure on success.
    NULL on failure.

--*/
{
    PMP_ADAPTER_RECEIVE_DPC ReceiveDpc=NULL, ExistingDpc=NULL;
    PLIST_ENTRY ReceiveListEntry;
    NTSTATUS Status = STATUS_SUCCESS;

    ASSERT(BlockId <  NIC_SUPPORTED_NUM_QUEUES);

    do
    {
        ReceiveDpc = NdisAllocateMemoryWithTagPriority(
                                Adapter->AdapterHandle,
                                sizeof(MP_ADAPTER_RECEIVE_DPC),
                                NIC_TAG_DPC,
                                NormalPoolPriority);
        if(!ReceiveDpc)
        {
            DEBUGP(MP_ERROR, "[%p] Could not allocate memory for receive DPC.\n", Adapter);
            break;
        }
        NdisZeroMemory(ReceiveDpc, sizeof(MP_ADAPTER_RECEIVE_DPC));

        ReceiveDpc->Adapter = Adapter;

        //
        // Initialize DPC data (entry, DPC function, target processor)
        //
        NdisInitializeListHead(&ReceiveDpc->Entry);
        KeInitializeDpc(&ReceiveDpc->Dpc, RXReceiveIndicateDpc, Adapter);

        //
        // Allocate the work item that's used if we're close to the DPC watchdog timer limit
        //
        ReceiveDpc->WorkItem = NdisAllocateIoWorkItem(Adapter->AdapterHandle);
        if(!ReceiveDpc->WorkItem)
        {
            DEBUGP(MP_ERROR, "[%p] Could not allocate work item for receive DPC.\n", Adapter);
            Status = NDIS_STATUS_RESOURCES;
            break;
        }

        //
        // Make sure the target DPC list starts getting processed as soon as it's queued even if was queued from
        // another processor.
        //
        KeSetImportanceDpc(&ReceiveDpc->Dpc, MediumHighImportance);

#if (NDIS_SUPPORT_NDIS620)
        {
            //
            // Use Win7 APIs to set the target processor
            //
            PROCESSOR_NUMBER DpcProcessor = {0};
            DpcProcessor.Number = (UCHAR)ProcessorNumber;
            DpcProcessor.Group = ProcessorGroup;
            Status = KeSetTargetProcessorDpcEx(&ReceiveDpc->Dpc, &DpcProcessor);
        }
#else
        //
        // Use Vista APIs to set target processor
        //
        KeSetTargetProcessorDpc(&ReceiveDpc->Dpc, (CCHAR)ProcessorNumber);
#endif
        if(!NT_SUCCESS(Status))
        {
            DEBUGP(MP_ERROR, "[%p] Failed setting KeSetTargetProcessorDpcEx. Status: 0x%08x\n", Adapter, Status);
            break;
        }
        ReceiveDpc->ProcessorGroup = ProcessorGroup;
        ReceiveDpc->ProcessorNumber = ProcessorNumber;

        //
        // Check if it's already on the list, if it is, return existing Dpc and free this one
        // otherwise, add to the adapter's DPC list.
        //
        NdisAcquireSpinLock(&Adapter->RecvDpcListLock);

        for(ReceiveListEntry = Adapter->RecvDpcList.Flink;
             ReceiveListEntry != &Adapter->RecvDpcList;
             ExistingDpc = NULL, ReceiveListEntry = ReceiveListEntry->Flink)
        {
            ExistingDpc = CONTAINING_RECORD(ReceiveListEntry, MP_ADAPTER_RECEIVE_DPC, Entry);
            if(ExistingDpc->ProcessorNumber == ProcessorNumber && ExistingDpc->ProcessorGroup == ProcessorGroup)
            {
                //
                // A DPC exists with matching affinity information. Break so that we don't NULL ExistingDpc
                //
                break;
            }
        }

        if(ExistingDpc)
        {
            //
            // Use existing DPC
            //
            DEBUGP(MP_TRACE, "[%p] Reusing existing DPC which matches affinity.\n", Adapter);
            NICFreeReceiveDpc(ReceiveDpc);
            ReceiveDpc = ExistingDpc;

        }
        else
        {
            //
            // Add new DPC to adapter's list
            //
            InsertTailList(&Adapter->RecvDpcList, &ReceiveDpc->Entry);
        }

        NdisReleaseSpinLock(&Adapter->RecvDpcListLock);

        NICReceiveDpcSetOwnership(ReceiveDpc,BlockId);

    }while(FALSE);

    if(!NT_SUCCESS(Status) && ReceiveDpc)
    {
        NICFreeReceiveDpc(ReceiveDpc);
        ReceiveDpc = NULL;
    }

    return ReceiveDpc;
}

VOID
NICFreeReceiveDpc(
    _In_ PMP_ADAPTER_RECEIVE_DPC AdapterDpc)
/*++
Routine Description:

    The NICFreeReceiveDpc function frees the passed in DPC. Should only be called when the DPC is not active (not queued, flushed).

Arguments:

    AdapterDpc                  DPC to free

    Return Value:

    None

--*/
{
    ASSERT(AdapterDpc->RecvBlockCount==0);
    //
    // Free DPC dymainc fields and memory
    //
    if(AdapterDpc->WorkItem)
    {
         NdisFreeIoWorkItem(AdapterDpc->WorkItem);
    }
    NdisFreeMemory(AdapterDpc, sizeof(MP_ADAPTER_RECEIVE_DPC), 0);
}

PMP_ADAPTER_RECEIVE_DPC
NICGetDefaultReceiveDpc(
    _In_ PMP_ADAPTER      Adapter,
    _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId)
/*++
Routine Description:

    The NICGetDefaultReceiveDpc function returns the default receive DPC for the adapter, and sets it to consume
    receives from the passed in QueueId.


Arguments:

    Adapter                     Pointer to the adapter that will own the DPC.
    QueueId                     Queue whose receives the DPC should consume (0 for non-VMQ scenarios)

    Return Value:

    PMP_ADAPTER_RECEIVE_DPC structure.

--*/
{
    ASSERT(BlockId <  NIC_SUPPORTED_NUM_QUEUES);

    if(VMQ_ENABLED(Adapter))
    {
        NdisAcquireSpinLock(&Adapter->RecvDpcListLock);
        NICReceiveDpcSetOwnership(Adapter->DefaultRecvDpc,BlockId);
        NdisReleaseSpinLock(&Adapter->RecvDpcListLock);
    }
    else
    {
        UNREFERENCED_PARAMETER(BlockId);
    }

    return Adapter->DefaultRecvDpc;
}

NDIS_STATUS
    NICAllocRCBData(
    _In_ PMP_ADAPTER Adapter,
    ULONG NumberOfRcbs,
    _Outptr_result_bytebuffer_(NumberOfRcbs * sizeof(RCB)) PVOID *RcbMemoryBlock,
    _Inout_ PLIST_ENTRY FreeRcbList,
    _Inout_ PNDIS_SPIN_LOCK FreeRcbListLock,
    _Inout_ PNDIS_HANDLE RecvNblPoolHandle
    )
/*++
Routine Description:

    The NICAllocRCBData function allocated NumberOfRcbs worth of RCB and NBL memory for use in receive indication,
    and populates the passed in FreeRcbList with this data.

    IRQL = PASSIVE_LEVEL

Arguments:

    Adapter                     Pointer to our adapter
    NumberOfRcbs                Number of RCB structures to allocate (and NBLs as a result)
    RcbMemoryBlock              Receives the allocated memory block that is split up into each individual RCB
    FreeRcbList                 Initialized blank list to be populated with each individual RCB.
    FreeRcbListLock             Initialized lock used when updating the RCB list (and subsequent consumers should use lock).
    RcbNblPoolHandle            Receives the Ndis NBL pool handle for the allocated NBLs (to be used on free).

    Return Value:

    NDIS_STATUS_xxx code

--*/
{
    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
    NET_BUFFER_LIST_POOL_PARAMETERS NblParameters;
    ULONG index;

    do
    {
        //
        // Allocate an NBL pool for receive indications.
        //

        {C_ASSERT(sizeof(NblParameters) >= NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1);}
        NblParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
        NblParameters.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
        NblParameters.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;

        NblParameters.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT; // always use DEFAULT for miniport drivers
        NblParameters.fAllocateNetBuffer = TRUE;
        NblParameters.ContextSize = 0;
        NblParameters.PoolTag = NIC_TAG_RECV_NBL;
        NblParameters.DataSize = 0;

        *RecvNblPoolHandle = NdisAllocateNetBufferListPool(
                NdisDriverHandle,
                &NblParameters);
        if (!*RecvNblPoolHandle)
        {
            Status = NDIS_STATUS_RESOURCES;
            DEBUGP(MP_ERROR, "[%p] NdisAllocateNetBufferListPool failed\n", Adapter);
            break;
        }

        //
        // Allocate receive memory block
        //
        *RcbMemoryBlock = NdisAllocateMemoryWithTagPriority(
                Adapter->AdapterHandle,
                sizeof(RCB) * NumberOfRcbs,
                NIC_TAG_RCB,
                NormalPoolPriority);

        if (!*RcbMemoryBlock)
        {
            Status = NDIS_STATUS_RESOURCES;
            DEBUGP(MP_ERROR, "[%p] NdisAllocateMemoryWithTagPriority failed\n", Adapter);
            break;
        }
        NdisZeroMemory(*RcbMemoryBlock, sizeof(RCB) * NumberOfRcbs);

        //
        // Split into individual RCBs, allocate NBL, and add to free list
        //
        for (index = 0; index < NumberOfRcbs; index++)
        {
            PRCB Rcb = &((PRCB)*RcbMemoryBlock)[index];

            //
            // Allocate an NBL with its single NET_BUFFER from the preallocated
            // pool.
            //
            Rcb->Nbl = NdisAllocateNetBufferAndNetBufferList(
                    *RecvNblPoolHandle,
                    0,     // ContextSize
                    0,     // ContextBackfill
                    NULL,  // MdlChain
                    0,     // DataOffset
                    0);    // DataLength
            if (Rcb->Nbl == NULL)
            {
                Status = NDIS_STATUS_RESOURCES;
                DEBUGP(MP_ERROR, "[%p] NdisAllocateNetBufferAndNetBufferList failed\n", Adapter);
                break;
            }

            //
            // Add RCB pointer to miniport reserved portion of NBL
            //
            RCB_FROM_NBL(Rcb->Nbl) = Rcb;

            NdisInterlockedInsertTailList(
                    FreeRcbList,
                    &Rcb->RcbLink,
                    FreeRcbListLock);
        }

    }while(FALSE);

    return Status;
}

NDIS_STATUS
NICReadRegParameters(
    _In_  PMP_ADAPTER Adapter)
/*++
Routine Description:

    Read device configuration parameters from the registry

Arguments:

    Adapter                         Pointer to our adapter
    WrapperConfigurationContext     For use by NdisOpenConfiguration

    Should be called at IRQL = PASSIVE_LEVEL.

Return Value:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_FAILURE
    NDIS_STATUS_RESOURCES

--*/
{
    NDIS_STATUS                   Status = NDIS_STATUS_SUCCESS;

    NDIS_CONFIGURATION_OBJECT     ConfigurationParameters;
    NDIS_HANDLE                   ConfigurationHandle;

    DEBUGP(MP_TRACE, "[%p] ---> NICReadRegParameters\n", Adapter);

    PAGED_CODE();

    //
    // Open the registry for this adapter to read advanced
    // configuration parameters stored by the INF file.
    //

    NdisZeroMemory(&ConfigurationParameters, sizeof(ConfigurationParameters));

    {C_ASSERT(sizeof(ConfigurationParameters) >= NDIS_SIZEOF_CONFIGURATION_OBJECT_REVISION_1);}
    ConfigurationParameters.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT;
    ConfigurationParameters.Header.Size = NDIS_SIZEOF_CONFIGURATION_OBJECT_REVISION_1;
    ConfigurationParameters.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1;

    ConfigurationParameters.NdisHandle = Adapter->AdapterHandle;
    ConfigurationParameters.Flags = 0;

    Status = NdisOpenConfigurationEx(
            &ConfigurationParameters,
            &ConfigurationHandle);
    if(Status != NDIS_STATUS_SUCCESS)
    {
        DEBUGP(MP_ERROR, "[%p] NdisOpenConfigurationEx Status = 0x%08x\n", Adapter, Status);
        return NDIS_STATUS_FAILURE;
    }


    //
    // Read all of our configuration parameters using NdisReadConfiguration
    // and parse the value.
    //
    NICSetMacAddress(Adapter, ConfigurationHandle);

    Adapter->ulLinkSendSpeed = NIC_XMIT_SPEED;
    Adapter->ulLinkRecvSpeed = NIC_RECV_SPEED;

    //
    // Read VMQ related configuration parameters
    //
    Status = ReadRxQueueConfig(ConfigurationHandle, Adapter);
    if(Status != NDIS_STATUS_SUCCESS)
    {
        DEBUGP(MP_ERROR, "[%p] ReadRxQueueConfig Status = 0x%08x\n", Adapter, Status);
        Status = NDIS_STATUS_FAILURE;
        goto Exit;
    }

    //
    // Read NDIS QOS related configuration parameters
    //
    Status = ReadQOSConfig(ConfigurationHandle, Adapter);
    if (Status != NDIS_STATUS_SUCCESS)
    {
        DEBUGP(MP_ERROR, "[%p] ReadQOSConfig Status = 0x%08x\n", Adapter, Status);
        Status = NDIS_STATUS_FAILURE;
        goto Exit;
    }

Exit:
    //
    // Close the configuration registry
    //
    NdisCloseConfiguration(ConfigurationHandle);

    DEBUGP(MP_TRACE, "[%p] <--- NICReadRegParameters Status = 0x%08x\n", Adapter, Status);
    return Status;
}


VOID
NICSetMacAddress(
    _In_  PMP_ADAPTER  Adapter,
    _In_  NDIS_HANDLE  ConfigurationHandle)
/*++
Routine Description:

    Configures the NIC with the correct permanent and current MAC addresses.
    If no permanent address is saved, generate a new one.

    IRQL = PASSIVE_LEVEL

Arguments:

    Adapter                     Pointer to our adapter
    ConfigurationHandle         NIC configuration from NdisOpenConfigurationEx

Return Value:

    None.

--*/
{
    NDIS_STATUS                   Status;
    PUCHAR                        NetworkAddress;
    UINT                          Length = 0;


    PAGED_CODE();


    HWReadPermanentMacAddress(
            Adapter,
            ConfigurationHandle,

            Adapter->PermanentAddress);


    //
    // Now seed the current MAC address with the permanent address.
    //
    NIC_COPY_ADDRESS(Adapter->CurrentAddress, Adapter->PermanentAddress);


    //
    // Read NetworkAddress registry value and use it as the current address
    // if there is a software configurable NetworkAddress specified in
    // the registry.
    //
    NdisReadNetworkAddress(
            &Status,
            &NetworkAddress,
            &Length,
            ConfigurationHandle);

    if ((Status == NDIS_STATUS_SUCCESS) && (Length == NIC_MACADDR_SIZE))
    {
        if ((NIC_ADDR_IS_MULTICAST(NetworkAddress)
                || NIC_ADDR_IS_BROADCAST(NetworkAddress))
                || !NIC_ADDR_IS_LOCALLY_ADMINISTERED(NetworkAddress))
        {
            DEBUGP(MP_ERROR, "[%p] Overriding NetworkAddress is invalid: ", Adapter);
            DbgPrintAddress(NetworkAddress);
        }
        else
        {
            NIC_COPY_ADDRESS(Adapter->CurrentAddress, NetworkAddress);
        }
    }

    DEBUGP(MP_LOUD, "[%p] Permanent Address = ", Adapter);
    DbgPrintAddress(Adapter->PermanentAddress);

    DEBUGP(MP_LOUD, "[%p] Current Address = ", Adapter);
    DbgPrintAddress(Adapter->CurrentAddress);
}


BOOLEAN
NICIsBusy(
    _In_  PMP_ADAPTER  Adapter)
/*++
Routine Description:

    The NICIsBusy function returns whether the NIC has pending receives or sends. Before calling this
    function, the NIC should be in a state where it will not receive new sends/receives (either datapath
    is stopped, or !MP_IS_READY). Otherwise, the NIC may create new pending receives and sends after the
    NICIsBusy call.

Arguments:

    Adapter                     Pointer to our adapter

Return Value:

    TRUE: Send or Receive path is not idle.
    FALSE: Send & Receive paths are idle.

--*/
{
    BOOLEAN fBusy = FALSE;
    DEBUGP(MP_TRACE, "[%p] ---> NICIsBusy\n", Adapter);

#if DBG
    //
    // Check whether we might get new sends/receives
    //
    if(MPIsAdapterAttached(Adapter))
    {
        //
        // Adapter is still attached to the datapath, so it should not be ready (otherwise receives could get queued after we've read the counters)
        //
        ASSERT(!MP_IS_READY(Adapter));
    }
#endif

    //
    // Check if all the NBLs that the protocol sent down for transmission have
    // been completed yet.
    //
    if (Adapter->nBusySend)
    {
        DEBUGP(MP_INFO, "[%p] Send path is not idle, nBusySend = %d", Adapter, Adapter->nBusySend);
        fBusy = TRUE;
    }
    else
    {
        //
        // Check if all of our NBLs that we indicated up to the protocol have
        // returned to us yet.
        //
        USHORT ReceiveBlockId = 0;
        for(;ReceiveBlockId<NIC_SUPPORTED_NUM_QUEUES; ++ReceiveBlockId)
        {
            if(RECEIVE_BLOCK_IS_BUSY(Adapter, ReceiveBlockId))
            {
                DEBUGP(MP_INFO, "[%p] Recv block %i not idle, nBusyRecv = %u\n", Adapter, ReceiveBlockId, Adapter->ReceiveBlock[ReceiveBlockId].PendingReceives);
                fBusy = TRUE;
                break;
            }
        }
    }

    DEBUGP(MP_TRACE, "[%p] <--- NICIsBusy fBusy = %u\n", Adapter, (UINT)fBusy);
    return fBusy;
}


NDIS_STATUS
NICInitializeReceiveBlock(
    _In_ PMP_ADAPTER Adapter,
    _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId)
/*++
Routine Description:

    The NICInitializeReceiveBlock function initializes the fields of an adapter receive block. This block
    is consumed by the owning receive DPC.


Arguments:

    Adapter      - Pointer to our Adapter
    BlockIndex   - Receive block to initialize

Return Value:

     NDIS_STATUS_xxx code.

--*/
{
    DEBUGP(MP_TRACE, "[%p] ---> NICInitializeReceiveBlock. Index: %i\n", Adapter, BlockId);
    ASSERT(BlockId <  NIC_SUPPORTED_NUM_QUEUES);

    NdisZeroMemory(&Adapter->ReceiveBlock[BlockId], sizeof(MP_ADAPTER_RECEIVE_BLOCK));
    NdisInitializeListHead(&Adapter->ReceiveBlock[BlockId].ReceiveList);
    NdisAllocateSpinLock(&Adapter->ReceiveBlock[BlockId].ReceiveListLock);

    DEBUGP(MP_TRACE, "[%p] <--- NICInitializeReceiveBlock\n", Adapter);

    return NDIS_STATUS_SUCCESS;

}

VOID
NICFlushReceiveBlock(
    _In_ PMP_ADAPTER Adapter,
    _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId)
/*++
Routine Description:

    This routine flushes all pending receives for a specific adapter receive block.

Arguments:

    Adapter              - Pointer to our adapter
    BlockIndex           - Receive block to flush
Return Value:

    None

--*/
{
    PLIST_ENTRY Entry;
    PMP_ADAPTER_RECEIVE_BLOCK ReceiveBlock;

    DEBUGP(MP_TRACE, "[%p] ---> NICFlushReceiveBlock. QueueId: %i\n", Adapter, BlockId);
    ASSERT(BlockId <  NIC_SUPPORTED_NUM_QUEUES);
    ReceiveBlock = &Adapter->ReceiveBlock[BlockId];

    //
    // Return any pending receives on the block
    //
    for(Entry = NdisInterlockedRemoveHeadList(&ReceiveBlock->ReceiveList, &ReceiveBlock->ReceiveListLock);
        Entry;
        Entry = NdisInterlockedRemoveHeadList(&ReceiveBlock->ReceiveList, &ReceiveBlock->ReceiveListLock))
    {
        ReturnRCB(Adapter, CONTAINING_RECORD(Entry, RCB, RcbLink));
    }

    DEBUGP(MP_TRACE, "[%p] <-- NICFlushReceiveBlock\n", Adapter);

}

NDIS_STATUS
NICReferenceReceiveBlock(
    _In_ PMP_ADAPTER Adapter,
    _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId)
/*++
Routine Description:

    This routine increments the pending receive count on a adapter receive block. This count is used
    throughout the code to determine whether there are pending operations that will use the adapter's resources such as
    receive buffers (which would pend operations such as adapter reset, VMQ queue free, etc...).

Arguments:

    Adapter              - Pointer to our adapter
    BlockIndex           - Receive block to flush

Return Value:

    NDIS_STATUS_SUCCESS if reference was acquired succesfully.
    NDIS_STATUS_ADAPTER_NOT_READY if the adapter state is such that we should not acquire new references to resources

--*/
{
    //
    // Increment the reference count before checking NIC state avoid race conditions with code that checks reference count. The
    // reference will be undone if the adapter is not ready.
    //
    ULONG RefCount = InterlockedIncrement(&Adapter->ReceiveBlock[BlockId].PendingReceives);

    //
    // Make sure the increment happens before ready state check
    //
    KeMemoryBarrier();

    //
    // If the adapter is not ready, undo the reference and fail the call
    //
    if(!MP_IS_READY(Adapter))
    {
        InterlockedDecrement(&Adapter->ReceiveBlock[BlockId].PendingReceives);
        DEBUGP(MP_LOUD, "[%p] Could not acquire reference for ReceiveBlock %i. The adapter is not ready.\n", Adapter, BlockId);
        return NDIS_STATUS_ADAPTER_NOT_READY;
    }

    DEBUGP(MP_LOUD, "[%p] Acquired reference for ReceiveBlock %i. RefCount: %i.\n", Adapter, BlockId, RefCount);

    return NDIS_STATUS_SUCCESS;
}

VOID
NICDereferenceReceiveBlock(
    _In_ PMP_ADAPTER Adapter,
    _In_ _In_range_(0, NIC_SUPPORTED_NUM_QUEUES-1) ULONG BlockId,
    _Out_opt_ ULONG *RefCount)
/*++
Routine Description:

    This routine decrements the pending receive count on a adapter receive block. This count is used
    throughout the code to determine whether there are pending operations that will use the adapter's resources such as
    receive buffers (which would pend operations such as adapter reset, VMQ queue free, etc...).

Arguments:

    Adapter              - Pointer to our adapter
    BlockIndex           - Receive block to flush

Return Value:

    None

--*/
{
    ULONG Count = InterlockedDecrement(&Adapter->ReceiveBlock[BlockId].PendingReceives);
    DEBUGP(MP_LOUD, "[%p] Released reference for ReceiveBlock %i. RefCount: %i.\n", Adapter, BlockId, Count);
    if(RefCount)
    {
        *RefCount = Count;
    }
}

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