Sample Code

windows driver samples/ Native Wi-Fi Miniport Sample Driver/ C++/ hvl/ vnic_main.c/

/*++

Copyright (c) Microsoft Corporation. All rights reserved.

Module Name:
    vnic_main.c

Abstract:
    Implements the PNP routines for the VNIC
    
Revision History:
      When        What
    ----------    ----------------------------------------------
    09-04-2007    Created

Notes:

--*/
#include "precomp.h"


#if DOT11_TRACE_ENABLED
#include "vnic_main.tmh"
#endif

#define INC_CTXS_REF(pVNic) InterlockedIncrement(&pVNic->CtxtSRefCount)
#define DEC_CTXS_REF(pVNic) InterlockedDecrement(&pVNic->CtxtSRefCount)

__inline
PSTR
GetPortTypeString(
    MP_PORT_TYPE PortType
    )
{
    PSTR pszString = NULL;
    
    switch (PortType)
    {
        case HELPER_PORT:
            pszString = "Helper ";
            break;
        case EXTSTA_PORT:
            pszString = "Extensible Station ";
            break;
        case EXTAP_PORT:
            pszString = "Extensible AP ";
            break;
    }

    return pszString;
}


_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_raises_(DISPATCH_LEVEL)
_At_((pVNic->Lock).OldIrql, _IRQL_saves_)
_Requires_lock_not_held_((pVNic->Lock).SpinLock)
_Acquires_lock_((pVNic->Lock).SpinLock)
extern
__inline
VOID
VNicLock(
    _In_  PVNIC      pVNic
    )
{
    ACQUIRE_LOCK(pVNic, FALSE);                                               
}

_Acquires_lock_((&pVNic->Lock)->SpinLock)
extern
__inline
VOID
VNicLockAtDispatch(
    _In_  PVNIC      pVNic,
    _In_  BOOLEAN    fDispatch
    )
#pragma warning (suppress:28167) // IRQL change
{
    ACQUIRE_LOCK(pVNic, fDispatch);                                               
}

_IRQL_requires_max_(DISPATCH_LEVEL)
_IRQL_requires_min_(DISPATCH_LEVEL)
_At_((pVNic->Lock).OldIrql, _IRQL_restores_)
_Requires_lock_held_((pVNic->Lock).SpinLock)
_Releases_lock_((pVNic->Lock).SpinLock)
extern
__inline
VOID
VNicUnlock(
    _In_  PVNIC      pVNic
    )
{
    #pragma prefast(disable : 6001)
    
    ASSERT(VNicIsLocked(pVNic));
    RELEASE_LOCK(pVNic, FALSE);

    #pragma prefast(enable: 6001)
}

_Requires_lock_held_((&pVNic->Lock)->SpinLock)
_Releases_lock_((&pVNic->Lock)->SpinLock)
extern
__inline
VOID
VNicUnlockAtDispatch(
    _In_  PVNIC      pVNic,
    _In_  BOOLEAN    fDispatch
    )
#pragma warning (suppress:28167) // IRQL change
{
    RELEASE_LOCK(pVNic, fDispatch);                                               
}

extern
__inline    
BOOLEAN
VNicIsActive(    
    _In_  PVNIC      pVNic
    )
{
    ASSERT(VNicIsLocked(pVNic));
    return pVNic->fActive;
}

__inline    
VOID
VNicSetActive(    
    _In_  PVNIC      pVNic,
    _In_  BOOLEAN fActive
    )
{
    ASSERT(VNicIsLocked(pVNic));
    pVNic->fActive = fActive;
}

extern
__inline    
BOOLEAN
VNicHasExAccess(    
    _In_  PVNIC      pVNic
    )
{
    ASSERT(VNicIsLocked(pVNic));
    return pVNic->fExAccess;
}

__inline    
VOID
VNicSetExAccess(    
    _In_  PVNIC      pVNic,
    _In_  BOOLEAN fExAccess
    )
{
    ASSERT(VNicIsLocked(pVNic));
    if (fExAccess)
    {
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): VNic now has exclusive access \n", VNIC_PORT_NO));
    }
    else
    {
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): VNic no longer has exclusive access \n", VNIC_PORT_NO));
    }
    pVNic->fExAccess = fExAccess;
}

// this function can be called only when the VNic is locked
extern
__inline    
VOID
VNicIncCtxSRef(
    _In_  PVNIC      pVNic,
    _In_  ULONG      ulNumRef, 
    _In_  VNIC_CTXS_REF_TYPE RefType
    )
{
    ASSERT(VNicIsLocked(pVNic));
    ASSERT(VNicIsActive(pVNic));

    ASSERT(pVNic->CtxtSRefCount >= 0);
    ASSERT(pVNic->CtxSRefCountTracker[RefType] >= 0);

    pVNic->CtxtSRefCount = pVNic->CtxtSRefCount + ulNumRef;
    pVNic->CtxSRefCountTracker[RefType] = pVNic->CtxSRefCountTracker[RefType] + ulNumRef;
}


extern
__inline    
VOID
VNicAddCtxSRef(
    _In_  PVNIC      pVNic,
    _In_  VNIC_CTXS_REF_TYPE RefType
    )
{
    VNicIncCtxSRef(pVNic, 1, RefType);
}



// this function can be called only when the VNic is locked
extern
__inline    
VOID
VNicDecCtxSRef(
    _In_  PVNIC      pVNic,
    _In_  ULONG      ulNumRef, 
    _In_  VNIC_CTXS_REF_TYPE RefType
    )
{
    ASSERT(pVNic->fLocked);
    pVNic->CtxtSRefCount = pVNic->CtxtSRefCount - ulNumRef;
    pVNic->CtxSRefCountTracker[RefType] = pVNic->CtxSRefCountTracker[RefType] - ulNumRef;

    ASSERT(pVNic->CtxtSRefCount >= 0);
    ASSERT(pVNic->CtxSRefCountTracker[RefType] >= 0);
}

extern
__inline    
VOID
VNicRemoveCtxSRef(
    _In_  PVNIC      pVNic,
    _In_  VNIC_CTXS_REF_TYPE RefType
    )
{
    VNicDecCtxSRef(pVNic, 1, RefType);
}

extern
__inline    
BOOLEAN
VNicIsLocked(    
    _In_  PVNIC      pVNic
    )
{
    return pVNic->fLocked;
}

extern
__inline
BOOLEAN
VNicIsInReset(
    _In_ PVNIC pVNic
    )
{
    ASSERT(VNicIsLocked(pVNic));
    return pVNic->fResetInProgress;
}

extern
__inline
BOOLEAN
VNicIsPreallocatedRequest(
    PVNIC pVNic,
    PVNIC_EX_ACCESS_REQ pExReq
    )
{
    return (pVNic->pPnpOpExReq == pExReq);
}

extern
__inline
BOOLEAN
VNicIsPreallocatedOperation(
    PVNIC pVNic,
    PPENDING_OP pPendingOp
    )
{
    return (&pVNic->PnpPendingOp== pPendingOp);
}

BOOLEAN
VNic11IsOkToCtxS(
    PVNIC pVNic
    )
{
    return pVNic->fVNicReadyToCtxS;
}

VOID
VNicSetReadyToCtxS(
    PVNIC pVNic,
    BOOLEAN fReadyToCtxS
    )
{
    if (fReadyToCtxS)
    {
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Marking the VNIC as ready to acquire context \n", VNIC_PORT_NO));
    }
    else
    {
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Marking the VNIC as not ready to acquire context \n", VNIC_PORT_NO));
    }
    
    pVNic->fVNicReadyToCtxS = fReadyToCtxS;
}

extern
__inline
NDIS_STATUS 
VNicCanProcessReqNow(PVNIC pVNic, BOOLEAN *pfProcessNow)
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;

    ASSERT(VNicIsLocked(pVNic));

    do
    {
        *pfProcessNow = FALSE;
        
        if (VNicIsInReset(pVNic))
        {
            ndisStatus = NDIS_STATUS_RESET_IN_PROGRESS;
            break;
        }
        
        *pfProcessNow = VNicIsActive(pVNic) && VNicIsPendingOpQueueEmpty(pVNic);
    } while (FALSE);

    return ndisStatus;
}

extern
__inline    
NDIS_STATUS
VNicPreHwSyncCallActions(PVNIC pVNic, BOOLEAN *pfProgramHw)
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    
    VNicLock(pVNic);

    do
    {
        ndisStatus = VNicCanProcessReqNow(pVNic, pfProgramHw);
        if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            break;
        }

        // add the context switch ref count for the call to the hardware
        if (*pfProgramHw)
        {
            VNicAddCtxSRef(pVNic, REF_HW_OP);
        }
    } while (FALSE);
    
    VNicUnlock(pVNic);

    return ndisStatus;
}

extern
__inline    
VOID
VNicPostHwSyncCallActions(PVNIC pVNic, BOOLEAN fProgramHw)
{
    if (fProgramHw)
    {
        VNicLock(pVNic);

        // remove the context switch ref count for the call to the hardware
        VNicRemoveCtxSRef(pVNic, REF_HW_OP);        

        VNicUnlock(pVNic);
    }
    
    return;
}

VOID
VNicWaitForCtxSRefAndMark(    
    _In_  PVNIC      pVNic,
    _In_  BOOLEAN    fMark
    )
{
    ULONG ulNumIter = 0;
    
    VNicLock(pVNic);
    
    while(pVNic->CtxtSRefCount >= 1)
    {
        if ( ++ulNumIter > 3000)
        {
            // 30 seconds have passed - something must be wrong
            ASSERTMSG("VNic reference count hasn't gone to 0 in last 30 seconds. Check driver state\n", FALSE);
        }
        
        VNicUnlock(pVNic);
        NdisMSleep(10 * 1000);  // 10 msec
        VNicLock(pVNic);
    }

    if (fMark)
    {
        VNicSetActive(pVNic, FALSE);
        /*
            A non helper port might be given exclusive access even when it hasn't requested it.
            e.g. a port gets exclusive access when a Pause is happening (the helper port calls 
            Hvl11ActivatePort). There is no corresponding callback from HVL to let the VNIC know
            that it no longer has exclusive access. Hence assume that if the context is moving
            away from you, you do not have exclusive access.
            A helper port on the other hand will explicitly request and release exclusive access
            through VNic11RequestExAccess and VNic11ReleaseExAccess
            */
        if (pVNic->PortType != HELPER_PORT)
        {
            VNicSetExAccess(pVNic, FALSE);
        }
    }
    
    VNicUnlock(pVNic);
}

VOID
VNicWaitForCtxSRefAndMarkInactive(    
    _In_  PVNIC      pVNic
    )
{
    VNicWaitForCtxSRefAndMark(pVNic, TRUE);
}

VOID
VNicWaitForCtxSRef(    
    _In_  PVNIC      pVNic
    )
{
    VNicWaitForCtxSRefAndMark(pVNic, FALSE);
}

VOID
VNicWaitForResetToComplete(    
    _In_  PVNIC      pVNic
    )
{
    ULONG ulNumIter = 0;
    
    VNicLock(pVNic);

    // wait for reset only after it has acquired exclusive access
    while(pVNic->fResetInProgress)
    {
        if ( ++ulNumIter > 3000)
        {
            // 30 seconds have passed - something must be wrong
            ASSERTMSG("VNic is processing reset since last 30 seconds. Check driver state\n", FALSE);
        }
        
        VNicUnlock(pVNic);
        NdisMSleep(10 * 1000);  // 10 msec
        VNicLock(pVNic);
    }

    VNicUnlock(pVNic);
}

_IRQL_requires_(DISPATCH_LEVEL)
VOID
VNicWaitForOutstandingSends(    
    _In_  PVNIC      pVNic
    )
{
    ULONG ulNumIter = 0;
    
    ASSERT(VNicIsLocked(pVNic));

    while(pVNic->lOutstandingSends >= 1)
    {
        if ( ++ulNumIter > 3000)
        {
            // 30 seconds have passed - something must be wrong
            ASSERTMSG("VNic outtanding sends count hasn't gone to 0 in last 30 seconds. Check driver state\n", FALSE);
        }

        _Analysis_assume_lock_held_(pVNic->Lock.SpinLock);
        VNicUnlock(pVNic);
        NdisMSleep(10 * 1000);  // 10 msec
        VNicLock(pVNic);
    }    
}

BOOLEAN
VNicOperationRequiresExAccess(PENDING_OP_TYPE OpType)
{
    if (PENDING_OP_EX_ACCESS_REQ == OpType   ||
      PENDING_OP_RESET_REQ == OpType         ||
      PENDING_OP_CH_SW_REQ == OpType         ||
      PENDING_OP_DEF_KEY == OpType           ||
      PENDING_OP_DESIRED_PHY_ID_LIST == OpType ||
      PENDING_OP_OPERATING_PHY_ID == OpType  ||
      PENDING_OP_NIC_POWER_STATE == OpType
      )
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

NDIS_STATUS
VNic11Allocate(
    _In_  NDIS_HANDLE             MiniportAdapterHandle,
    _Outptr_result_maybenull_ PVNIC*        ppVNic,
    _In_  PMP_PORT                pPort
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    PVNIC pVNic = NULL;
    NDIS_HANDLE pendingOpsWorkItemHandle = NULL;
    BOOLEAN fFreeWorkItemHandle = FALSE;
    PVNIC_EX_ACCESS_REQ pExReq = NULL;
    
    do 
    {
        if ( NULL == ppVNic)
        {
            ndisStatus = NDIS_STATUS_INVALID_PARAMETER;
            MpTrace(COMP_HVL, DBG_SERIOUS, ("ppVNic is NULL"));
            break;
        }
        
        *ppVNic = NULL;
            
        ndisStatus = ALLOC_MEM(MiniportAdapterHandle, sizeof(VNIC), &pVNic);
        if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("Failed to allocate memory for a new VNIC"));
            break;
        }

        // the list heads should be the first ones to be initialized. This allows us to free things 
        // correctly if Free is called without Initialize being called in between        
        InitializeListHead(&pVNic->PendingOpQueue);

        // Allocate the pending operations work item 
        pendingOpsWorkItemHandle = NdisAllocateIoWorkItem(MiniportAdapterHandle);
        if(NULL == pendingOpsWorkItemHandle)
        {
            MpTrace (COMP_HVL, DBG_SERIOUS, ("NdisAllocateIoWorkItem failed"));
            ndisStatus = NDIS_STATUS_RESOURCES;
            break;
        }
        fFreeWorkItemHandle = TRUE;

        // pre-allocate the exclusive access request structure for PnP related exclusive accesses        
        ndisStatus = ALLOC_MEM(MiniportAdapterHandle, sizeof(VNIC_EX_ACCESS_REQ), &pExReq);
        if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("Failed to allocate memory for exclusive access request"));
            break;
        }

        // Allocate memory for fields inside the VNIC structure
        NdisAllocateSpinLock(&(pVNic->Lock));

        InitPktQueue(&pVNic->TxQueue);

        pVNic->MiniportAdapterHandle = MiniportAdapterHandle;
        
        pVNic->PendingOpsWorkItemHandle = pendingOpsWorkItemHandle;
        
        // Save the pointers in the VNIC
        pVNic->pvPort = pPort;
        pVNic->pPnpOpExReq = pExReq;
    
        *ppVNic = pVNic;

        MpTrace(COMP_HVL, DBG_NORMAL, ("Allocated a new VNIC %p", pVNic));        
    } while (FALSE);
    
    if (ndisStatus != NDIS_STATUS_SUCCESS)
    {
        if (NULL != pExReq)
        {
            FREE_MEM(pExReq);
        }
        if (fFreeWorkItemHandle)
        {
            NdisFreeIoWorkItem(pendingOpsWorkItemHandle);
        }
        if (NULL != pVNic)
        {
            FREE_MEM(pVNic);
        }
    }

    return ndisStatus;
}

VOID
VNic11Free(
    _In_  PVNIC                   pVNic
    )
{
    DeInitPktQueue(&pVNic->TxQueue);
    
    NdisFreeSpinLock(&(pVNic->Lock));

    if (pVNic->PendingOpsWorkItemHandle)
    {
        NdisFreeIoWorkItem(pVNic->PendingOpsWorkItemHandle);
    }

    if (pVNic->pPnpOpExReq)
    {
        FREE_MEM(pVNic->pPnpOpExReq);
    }
    
    FREE_MEM(pVNic);

    MpTrace(COMP_HVL, DBG_NORMAL, ("Freed the VNIC %p", pVNic));        
}

NDIS_STATUS
VNic11Initialize(
    _In_  PVNIC                   pVNic,
    _In_  PVOID                   pvHvl,
    _In_  PVOID                   pvHw,
    _In_  MP_PORT_TYPE            PortType,
    _In_  NDIS_PORT_NUMBER        PortNumber
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    PHW_MAC_CONTEXT pHwCtx = NULL;
    BOOLEAN fRegistered = FALSE;
    
    do
    {
        pVNic->pvHvl = pvHvl;
        pVNic->pvHw = pvHw;
        pVNic->PortType = PortType;        
        pVNic->CtxtSRefCount = 0;        // Initial RefCount = 0
        pVNic->fPendingOpsRunning = FALSE;
        pVNic->lOutstandingSends = 0;
        pVNic->fActive = FALSE;          // Port starts in inactive state
        pVNic->fExAccess = FALSE; 
        pVNic->fResetInProgress = FALSE;
        
        pVNic->PortNumber = PortNumber;

        VNicInitPreAllocatedOp(pVNic);
        
        VNicSetReadyToCtxS(pVNic, FALSE);
        NdisInitializeEvent(&pVNic->PendingOpWorkItemDoneEvent);
        
        /*
            This event is initialized to be set when the VNIC starts. Whenever the VNIC starts
            a pending operation VNIC, this event is reset. The event is set again whenever the
            pending ops work item completes
            */
        NdisSetEvent(&pVNic->PendingOpWorkItemDoneEvent); 
        
        // Allocate some MAC specific context structure in the HW
        ndisStatus = Hw11AllocateMACContext(pvHw, &pHwCtx, pVNic, PortNumber);
        if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): Hw11AllocateMACContext failed %!x!", VNIC_PORT_NO, ndisStatus));
            break;
        }

        pVNic->pvHwContext = pHwCtx;

        if (PortType == HELPER_PORT)
        {
            ndisStatus = Hvl11RegisterHelperPort(pvHvl, pVNic);
        }
        else
        {
            ndisStatus = Hvl11RegisterVNic(pvHvl, pVNic);
        }
        
        if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): Hvl11RegisterVNic failed %!x!", VNIC_PORT_NO, ndisStatus));
            break;
        }

        fRegistered = TRUE;

        MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%p, port number = %d, type=%s): Initialized\n", pVNic, PortNumber, GetPortTypeString(PortType)));
        
    } while (FALSE);

    if (NDIS_STATUS_SUCCESS != ndisStatus)
    {
        if (fRegistered)
        {
            if (pVNic->PortType == HELPER_PORT)
            {
                Hvl11DeregisterHelperPort(pVNic->pvHvl, pVNic);
            }
            else
            {
                Hvl11DeregisterVNic(pVNic->pvHvl, pVNic);
            }    
        }

        if (pHwCtx)
        {
            Hw11FreeMACContext(pVNic->pvHw, pVNic->pvHwContext);
        }
    }
    
    return ndisStatus;
}

VOID
VNic11UpdatePortType(
    _In_  PVNIC                   pVNic,
    _In_  MP_PORT_TYPE            PortType
    )
{
    MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): Updating port type %s  ---> %s\n", VNIC_PORT_NO, GetPortTypeString(pVNic->PortType), GetPortTypeString(PortType)));
    pVNic->PortType = PortType;
}

VOID
VNic11Terminate(
    _In_  PVNIC                   pVNic
    )
{
    MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): Terminate called\n", VNIC_PORT_NO));
    
    VNicLock(pVNic);
    pVNic->fTerminating = TRUE;
    VNicUnlock(pVNic);
    
    if (pVNic->PortType == HELPER_PORT)
    {
        Hvl11DeregisterHelperPort(pVNic->pvHvl, pVNic);
    }
    else
    {
        Hvl11DeregisterVNic(pVNic->pvHvl, pVNic);
    }
    
    // wait for context switch ref count to go to zero 
    VNicWaitForCtxSRef(pVNic);

    // wait for any pending work items to complete
    NdisWaitEvent(&pVNic->PendingOpWorkItemDoneEvent, 0);
    ASSERT(!pVNic->fPendingOpsRunning);
        
    // Free the MAC context that has been previously allocated in the HW for this port
    if (pVNic->pvHwContext)
    {
        Hw11FreeMACContext(pVNic->pvHw, pVNic->pvHwContext);
    }

    // free the memory allocated for any pending operations
    VNicDeleteAllPendingOperations(pVNic);
}

NDIS_STATUS
VNic11Pause(
    _In_  PVNIC                   pVNic
    )
{
    MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): Pause called \n", VNIC_PORT_NO));

    VNicLock(pVNic);

    // the port should have gotten us active before calling us
    ASSERT(VNicIsActive(pVNic) || (HELPER_PORT == pVNic->PortType));

    // cancel any pending sends in our queue
    VNicCancelPendingSends(pVNic);

    // Wait for the hardware to complete all our sends
    VNicWaitForOutstandingSends(pVNic);

    VNicUnlock(pVNic);
    
    Hw11PauseMACContext(pVNic->pvHwContext);
    
    return NDIS_STATUS_SUCCESS;
}

NDIS_STATUS
VNic11Restart(
    _In_  PVNIC                   pVNic
    )
{
    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Restart called \n", VNIC_PORT_NO));

    Hw11RestartMACContext(pVNic->pvHwContext);
    
    // nothing to do
    return NDIS_STATUS_SUCCESS;
}

_IRQL_requires_(DISPATCH_LEVEL)
NDIS_STATUS
VNicPerformReset(
    PVNIC pVNic,
    PDOT11_RESET_REQUEST pDot11ResetReq
)
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    BOOLEAN fUnlocked = FALSE;

    ASSERT(VNicIsLocked(pVNic));

    do
    {
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Performing VNIC Reset items\n", VNIC_PORT_NO));

        // BUGBUG: Once the send queue is merged with the pending ops, we don't need to cancel any pending sends
        // cancel any pending sends in our queue
        VNicCancelPendingSends(pVNic);

        // Wait for the hardware to complete all our sends
        VNicWaitForOutstandingSends(pVNic);

        _Analysis_assume_lock_held_(pVNic->Lock.SpinLock);
        VNicUnlock(pVNic);
        fUnlocked = TRUE;
        
        // wait for all the other references to go away
        VNicWaitForCtxSRef(pVNic);
                
        // now ask the hardware to reset itself
        ndisStatus = Hw11Dot11Reset(pVNic->pvHwContext, pDot11ResetReq);
        if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): Hw11Reset failed %!x!\n", VNIC_PORT_NO, ndisStatus));
            break;
        }        
    } while (FALSE);

    if (fUnlocked)
    {
        VNicLock(pVNic);
    }
    
    return ndisStatus;
}


NDIS_STATUS
VNic11Dot11Reset(
    _In_  PVNIC                   pVNic,
    _In_  PDOT11_RESET_REQUEST    Dot11ResetRequest    
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    VNIC_REQ Req;
    BOOLEAN fLocked = FALSE;
    PRESET_REQ pResetReq = NULL;
    BOOLEAN fFreeResetReq = FALSE;

    do
    {
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Reset called\n", VNIC_PORT_NO));

        VNicInitReq(&Req);

        ndisStatus = VNicAllocateResetReq(pVNic, &Req, Dot11ResetRequest, &pResetReq);
        if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("VNIC(%d): VNicAllocateResetReq failed %!x!\n", VNIC_PORT_NO, ndisStatus));
            break;
        }
        fFreeResetReq = TRUE;
        
        ndisStatus = VNicHandleExAccessOp(pVNic, PENDING_OP_RESET_REQ, pResetReq, FALSE, FALSE);
        MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): VNicHandleExAccessOp called %!x!\n", VNIC_PORT_NO, ndisStatus));

        // wait for us to get exclusive access and all the pending operations to complete
        if (NDIS_STATUS_PENDING == ndisStatus)
        {
            // the pending ops item would free the reset request
            fFreeResetReq = FALSE;

            VNicWaitForReq(&Req);            
            ndisStatus = VNicGetReqStatus(&Req);
        }
        else if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("VNIC(%d): VNicHandleExAccessOp failed %!x!\n", VNIC_PORT_NO, ndisStatus));
            break;
        }
        
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): The pending reset request was completed with status %!x!\n", VNIC_PORT_NO, ndisStatus));

        VNicLock(pVNic);
        fLocked = TRUE;

        ndisStatus = VNicPerformReset(pVNic, Dot11ResetRequest);
    } while (FALSE);

    if (NDIS_FAILURE(ndisStatus))
    {
        // clear the reset flag
        pVNic->fResetInProgress = FALSE;        
    }
    
    if (fLocked)
    {
        VNicUnlock(pVNic);
    }

    if (fFreeResetReq)
    {
        VNicFreeResetReq(pResetReq);
    }
    
    return ndisStatus;
}

NDIS_STATUS
VNic11Dot11ResetComplete(
    _In_  PVNIC                   pVNic
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;

    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): ResetComplete called\n", VNIC_PORT_NO));

    VNicLock(pVNic);

    pVNic->fResetInProgress = FALSE;

    VNicSetReadyToCtxS(pVNic, FALSE);
    
    // done with the reset operation. Release exclusive access
    VNicReleaseExAccess(pVNic);

    VNicUnlock(pVNic);

    /*
        Invalidate any channel & media notifications we may have sent out earlier. 0 means no preferred channel
        */
    VNicSendChannelNotification(pVNic, 0);
    VNicSendLinkStateNotification(pVNic, FALSE);

    return ndisStatus;
}

VOID
VNic11CtxSFromVNic(    
    _In_  PVNIC      pVNic,
    _In_  ULONG       ulFlags
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    BOOLEAN fConnected = FALSE;
    PVOID pvHwMacCtx = pVNic->pvHwContext;

    UNREFERENCED_PARAMETER(ulFlags);

    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Hvl asked to move context away from us \n", VNIC_PORT_NO));

    // wait for the context switch ref count to go to zero and mark ourselves inactive
    VNicWaitForCtxSRefAndMarkInactive(pVNic);

    // Wait for any ongoing reset to complete. If we do not wait, it is possible that we would 
    // become inactive (and lose exclusive access) before the reset processing is complete.
    VNicWaitForResetToComplete(pVNic);
    
    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Done waiting for outstanding references.\n", VNIC_PORT_NO));
    
    // send a null packet to the AP with the power save bit set, if we are currently 
    // connected to an infrastructure network
    if (pVNic->PortType == EXTSTA_PORT && (dot11_BSS_type_independent != Hw11QueryCurrentBSSType(pVNic->pvHwContext)))
    {
        fConnected = Hw11IsConnected(pvHwMacCtx);
        if (fConnected)
        {
            Hw11SendNullPkt(pvHwMacCtx, TRUE);
        }
    }
    
    ndisStatus = Hw11DisableMACContext(pVNic->pvHw, pVNic->pvHwContext);
    ASSERT(NDIS_STATUS_SUCCESS == ndisStatus);

    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Disabled the hardware MAC context.\n", VNIC_PORT_NO));
}

VOID
VNicHandlePendingConnReq(
    PVNIC pVNic
    )
{
    do
    {    
        if (VNicIsInReset(pVNic))
        {
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): Discarding a pending connection start operation since the VNIC is in reset\n", VNIC_PORT_NO));
            break;
        }
        
        VNicAddCtxSRef(pVNic, REF_CTXS_BARRIER);
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): Completed a pending connection start operation\n", VNIC_PORT_NO));

    } while (FALSE);
}

_IRQL_requires_(DISPATCH_LEVEL)
VOID
VNicHandlePendingJoinReq(
    PVNIC pVNic,
    PJOIN_REQ pJoinReq
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_PENDING;
    PVNIC_COMPLETION_CTX pVNicCompletionCtx = NULL;
    
    do
    {    
        if (VNicIsInReset(pVNic))
        {
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): VNic is in reset. Completing a pending join request with failure\n", VNIC_PORT_NO));
            ndisStatus = NDIS_STATUS_RESET_IN_PROGRESS;
            break;
        }

        ndisStatus = ALLOC_MEM(pVNic->MiniportAdapterHandle, sizeof(VNIC_COMPLETION_CTX), &pVNicCompletionCtx);
        if (NDIS_FAILURE(ndisStatus))
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): Failed to allocate memory for a new completion context \n", VNIC_PORT_NO));
            break;
        }
        
        pVNicCompletionCtx->CompletionFn = pJoinReq->CompletionHandler;
        
        ndisStatus = VNicJoinBSSHelper(
                    pVNic, 
                    pJoinReq->BSSDescription, 
                    pJoinReq->JoinFailureTimeout, 
                    pVNicCompletionCtx
                    );

        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): VNicJoinBSSHelper returned %!x!\n", VNIC_PORT_NO, ndisStatus));

    } while (FALSE);
    
    if (NDIS_STATUS_PENDING != ndisStatus)
    {
        VNicJoinComplete(pVNic, FALSE, pJoinReq->CompletionHandler, &ndisStatus);
        
        if (pVNicCompletionCtx)
        {
            FREE_MEM(pVNicCompletionCtx);
        }
    }
    
    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Completed a pending Join operation\n", VNIC_PORT_NO));
}

_IRQL_requires_(DISPATCH_LEVEL)
VOID
VNicHandlePendingStartBSSReq(
    PVNIC pVNic,
    PSTART_BSS_REQ pStartBss 
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_PENDING;
    
    do
    {    
        if (VNicIsInReset(pVNic))
        {
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): Discarding a pending start BSS operation since the VNIC is in reset\n", VNIC_PORT_NO));
            ndisStatus = NDIS_STATUS_RESET_IN_PROGRESS;
            break;
        }
        
        ndisStatus = VNicStartBSSHelper(
                    pVNic, 
                    pStartBss->BSSDescription
                    );
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): VNicStartBSSHelper returned %!x!\n", VNIC_PORT_NO, ndisStatus));

    } while (FALSE);
    
    if (NDIS_STATUS_PENDING != ndisStatus)
    {
        // notify the port that the startBss operation has completed
        if (pStartBss->CompletionHandler)
        {
            _Analysis_assume_lock_held_(pVNic->Lock.SpinLock);
            VNicUnlock(pVNic);
            pStartBss->CompletionHandler(pVNic->pvPort, &ndisStatus);
            VNicLock(pVNic);
        }
    }
    
    MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): Completed a pending start BSS operation %!x!\n", VNIC_PORT_NO, ndisStatus));
}

_IRQL_requires_(DISPATCH_LEVEL)
VOID
VNicHandlePendingStopBSSReq(
    PVNIC pVNic,
    PSTOP_BSS_REQ pStopBss 
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_PENDING;
    
    do
    {    
        if (VNicIsInReset(pVNic))
        {
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): Discarding a pending stop BSS operation since the VNIC is in reset\n", VNIC_PORT_NO));
            ndisStatus = NDIS_STATUS_RESET_IN_PROGRESS;
            break;
        }
        
        ndisStatus = VNicStopBSSHelper(pVNic);
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): VNicStopBSSHelper returned %!x!\n", VNIC_PORT_NO, ndisStatus));

    } while (FALSE);

    if (NDIS_STATUS_PENDING != ndisStatus)
    {
        // notify the port that the stopBss operation has completed
        if (pStopBss->CompletionHandler)
        {
            _Analysis_assume_lock_held_((& pVNic->Lock)->SpinLock);
            VNicUnlock(pVNic);
            pStopBss->CompletionHandler(pVNic->pvPort, &ndisStatus);
            VNicLock(pVNic);
        }
    }
    
    MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): Completed a pending stop BSS operation %!x!\n", VNIC_PORT_NO, ndisStatus));    
}

/*
    This routine processes all the pending operations in the VNIC. It goes through the pending
    operations in a loop, completing each one by one. It does give up the VNIC lock as part of
    the processing e.g. to callback into the port or call into the hardware. However that is fine 
    because we always manipulate the list under the VNIC lock 
    */
_Function_class_(NDIS_IO_WORKITEM_FUNCTION)
VOID
VNicPendingOpsWorkItem(
    _In_ PVOID            Context,
    _In_ NDIS_HANDLE      NdisIoWorkItemHandle
    )
{
    PVNIC pVNic = NULL;
    LIST_ENTRY *pEntryOp = NULL;
    PPENDING_OP pOp = NULL;
    PVOID pvPort = NULL;
    BOOLEAN fExAccessOpFound = FALSE;
    BOOLEAN fOpsSkipped = FALSE;
    PENDING_OP_TYPE Type;
    
    UNREFERENCED_PARAMETER(NdisIoWorkItemHandle);

    pVNic = (PVNIC)Context;

    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): VNicPendingOpsWorkItem called\n", VNIC_PORT_NO));

    VNicLock(pVNic);

    pvPort = pVNic->pvPort;

    pEntryOp = pVNic->PendingOpQueue.Flink;
    while (pEntryOp != &pVNic->PendingOpQueue)
    {
        pOp = CONTAINING_RECORD(pEntryOp, PENDING_OP, Link);

        /*
            If we are not being granted exclusive access and the operation requires it, we should
            stop doing more processing for now. The HVL will call us again with the exclusive
            access and these operations can be processed then. Note that we can not simply
            skip over these operations since all the operations must be performed in a sequence
            */
        if (!VNicHasExAccess(pVNic) && VNicOperationRequiresExAccess(pOp->Type))
        {
            fOpsSkipped = TRUE;
            break;
        }

        // This item can now be removed from the list        
        RemoveEntryList (&pOp->Link);

        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Found a pending %s operation\n", VNIC_PORT_NO, GetPendingOpString(pOp->Type)));

        Type = pOp->Type;
        
        switch (Type)
        {
            case PENDING_OP_CONN_START:
                VNicHandlePendingConnReq(pVNic);
                break;

            case PENDING_OP_JOIN_REQ:
            {
                VNicHandlePendingJoinReq(pVNic, pOp->pvOpData);
                break;
            }

            case PENDING_OP_START_BSS_REQ:
            {
                VNicHandlePendingStartBSSReq(pVNic, pOp->pvOpData);
                break;
            }

            case PENDING_OP_STOP_BSS_REQ:
            {
                VNicHandlePendingStopBSSReq(pVNic, pOp->pvOpData);
                break;
            }

            case PENDING_OP_EX_ACCESS_REQ:
            case PENDING_OP_RESET_REQ:
            case PENDING_OP_CH_SW_REQ:
            case PENDING_OP_DEF_KEY:
            case PENDING_OP_DESIRED_PHY_ID_LIST:
            case PENDING_OP_OPERATING_PHY_ID:
            case PENDING_OP_NIC_POWER_STATE:
            {
                ASSERT(VNicHasExAccess(pVNic));
                fExAccessOpFound = TRUE;
                VNicCompleteExclusiveAccessOp(pVNic, pOp->Type, pOp->pvOpData, TRUE);
                break;
            }

            default:
                ASSERT(FALSE);
        };

        VNicDeletePendingOperation(pVNic, pOp);

        // we should be locked again
        ASSERT(VNicIsLocked(pVNic));

        if (Type == PENDING_OP_EX_ACCESS_REQ && (pVNic->PortType == HELPER_PORT))
        {
            // This is a special case for the helper port. The helper port needs to serialize all
            // its exclusive access request callbacks. The HVL will call the VNIC separately for
            // each exclusive access request. The VNIC needs to do its job and complete no
            // more than one exclusive access request each time
            break;
        }
        
        pEntryOp = pVNic->PendingOpQueue.Flink;
    }

    /*
        BUGBUG 
        Handle any sends that had been queued. However if we bailed before completing all the
        operations then we should not handle the sends since they might rely on one of the 
        operations e.g. channel switch. This special casing is an artifact of the fact that we have
        separate queues for pending ops and data. We should perhaps merge the two
        */
    if (!fOpsSkipped && !PktQueueIsEmpty(&pVNic->TxQueue))
    {
        VNicProcessQueuedPkts(pVNic, FALSE);
    }
    else if (fOpsSkipped)
    {
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Not processing sends since some operations were skipped.\n", VNIC_PORT_NO));
    }

    // remove the context switch ref count added for this work item
    if (pVNic->PortType != HELPER_PORT)
    {
        VNicRemoveCtxSRef(pVNic, REF_PENDING_OP_WORKITEM);
    }
    
    pVNic->fPendingOpsRunning = FALSE;

    NdisSetEvent(&pVNic->PendingOpWorkItemDoneEvent);

    // unlock before we leave
    VNicUnlock(pVNic);
}

VOID
VNic11CtxSToVNic(    
    _In_  PVNIC       pVNic,
    _In_  ULONG       ulFlags
    )
{
    BOOLEAN fLocked = FALSE, fConnected = FALSE;

    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): VNic11CtxSToVNic called\n", VNIC_PORT_NO));

    do
    {
        VNicLock(pVNic);
        fLocked = TRUE;

        if (pVNic->fTerminating)
        {
            // can this happen?
            ASSERT(FALSE);
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): The VNIC is terminating. Simply returning the call\n", VNIC_PORT_NO));
            break;
        }
        
        VNicSetActive(pVNic, TRUE);

        // send a null packet to the AP with the power save bit not set, if we are currently 
        // connected to an infrastructure network
        if (pVNic->PortType == EXTSTA_PORT && (dot11_BSS_type_independent != Hw11QueryCurrentBSSType(pVNic->pvHwContext)))
        {
            fConnected = Hw11IsConnected(pVNic->pvHwContext);
            if (fConnected)
            {
                Hw11SendNullPkt(pVNic->pvHwContext, FALSE);
            }
        }
    
        if ( (VNIC_FLAG_HVL_ACTIVATED & ulFlags) && (pVNic->PortType == HELPER_PORT) && pVNic->fPendingOpsRunning)
        {
            /*
                This is an activation triggered by the helper port itself. The pending operation
                work item must be already running
                */
            ASSERT(pVNic->fPendingOpsRunning);

            MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): The helper port is activating itself. Not launching the pending operations workitem\n", VNIC_PORT_NO));
            break;
        }

        // wait for any pending work items already running
        if (pVNic->fPendingOpsRunning)
        {
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): A pending operations work item is already running. Waiting...\n", VNIC_PORT_NO));

            VNicUnlock(pVNic);
            NdisWaitEvent(&pVNic->PendingOpWorkItemDoneEvent, 0);
            VNicLock(pVNic);

            MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Finished waiting for the pending operations work item \n", VNIC_PORT_NO));

            ASSERT(!pVNic->fPendingOpsRunning);
        }

        /*
            If the HVL gave us exclusive access set our internal state to reflect that. Note that
            this should be done only after any pending work items have completed so as to not
            confuse the already running work item thread
            */
        if (VNIC_FLAG_GRANTED_EX_ACCESS & ulFlags)
        {
            VNicSetExAccess(pVNic, TRUE);
        }
        
        /*
            IMPORTANT: Check the queues only after we have waited for pending work items to
            complete. This is because the pending work item will empty the queue before 
            returning
            */            
        if (!VNicIsPendingOpQueueEmpty(pVNic) || !PktQueueIsEmpty(&pVNic->TxQueue))
        {                
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Launching a work item for pending operations\n", VNIC_PORT_NO));

            // add a context switch ref count for the pending operations work item
            
            /*
                special casing for the helper port. The helper port can context switch out while 
                the pending operations work item is running
                */
            if (pVNic->PortType != HELPER_PORT)
            {
                VNicAddCtxSRef(pVNic, REF_PENDING_OP_WORKITEM);
            }
            
            // reset the done event before launching the work item
            NdisResetEvent(&pVNic->PendingOpWorkItemDoneEvent);
            pVNic->fPendingOpsRunning = TRUE;
            
            // schedule the work item for pending operations
            NdisQueueIoWorkItem(
                pVNic->PendingOpsWorkItemHandle,
                VNicPendingOpsWorkItem,
                pVNic
                );
        }
    } while (FALSE);

    if (fLocked)
    {
        VNicUnlock(pVNic);
    }
}

VOID
VNic11ProgramHw(    
    _In_  PVNIC      pVNic,
    _In_  ULONG       ulFlags
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;

    UNREFERENCED_PARAMETER(ulFlags);

    // activate the hardware context
    ndisStatus = Hw11EnableMACContext(pVNic->pvHw, pVNic->pvHwContext);
    if (NDIS_STATUS_SUCCESS != ndisStatus)
    {
        MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): Hw11EnableMACContext failed %!x! \n", VNIC_PORT_NO, ndisStatus));
    }
}

NDIS_STATUS 
VNic11ReqExAccess(
    PVNIC pVNic,
    PORT11_GENERIC_CALLBACK_FUNC pCallbkFn,
    PVOID pvCtx,
    BOOLEAN fPnPOperation
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    PVNIC_EX_ACCESS_REQ pExReq = NULL;
    
    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): VNic was asked to request exclusive access \n", VNIC_PORT_NO));

    do
    {
        ndisStatus = VNicAllocateExAccessReq(pVNic, pCallbkFn, pvCtx, fPnPOperation, &pExReq);
        if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): VNicAllocateExAccessReq failed %!x!\n", VNIC_PORT_NO, ndisStatus));
            break;
        }

        ndisStatus = VNicHandleExAccessOp(pVNic, PENDING_OP_EX_ACCESS_REQ, pExReq, FALSE, fPnPOperation);
        MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): VNicHandleExAccessOp called %!x!\n", VNIC_PORT_NO, ndisStatus));

        if (NDIS_STATUS_PENDING != ndisStatus)
        {
            VNicFreeExAccessReq(pVNic, pExReq);
        }

    } while (FALSE);
    
    return ndisStatus;
}

NDIS_STATUS 
VNicReleaseExAccess(
    PVNIC pVNic
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;

    ASSERT(VNicIsLocked(pVNic));

    ndisStatus = Hvl11ReleaseExAccess(pVNic->pvHvl, pVNic);
    ASSERT(!NDIS_FAILURE(ndisStatus));
    
    if (NDIS_STATUS_SUCCESS == ndisStatus)
    {
        // the HVL has marked us as not having exclusive access
        VNicSetExAccess(pVNic, FALSE);
    }
    else if (NDIS_STATUS_PENDING == ndisStatus)
    {
        ndisStatus = NDIS_STATUS_SUCCESS;
    }

    return ndisStatus;
}


NDIS_STATUS 
VNic11ReleaseExAccess(
    PVNIC pVNic
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;

    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): VNic was asked to release exclusive access \n", VNIC_PORT_NO));

    VNicLock(pVNic);    
    ASSERT(VNicHasExAccess(pVNic));
    ndisStatus = VNicReleaseExAccess(pVNic);    
    VNicUnlock(pVNic);

    return ndisStatus;
}

BOOLEAN
VNic11AreCompatibleSignatures(
    PVNIC_SIGNATURE pSig1,
    PVNIC_SIGNATURE pSig2
    )
{
    BOOLEAN fCompatible = FALSE;

    do
    {
        if (pSig1->ulPhyId != pSig2->ulPhyId)
        {
            break;
        }

        if (pSig1->ulChannel != pSig2->ulChannel)
        {
            break;
        }

        /*
            the signatures can be incompatible only if both of them have non overlapping 
            default keys
            */
        if (pSig1->ucDefKeyMask & pSig2->ucDefKeyMask)
        {
            break;
        }

        fCompatible = TRUE;
    } while (FALSE);

//exitPt:    
    return fCompatible;
}

VNIC_SIGNATURE
VNic11GetSignature(
    PVNIC pVNic
    )
{
    VNIC_SIGNATURE sig = {0};
    PVOID pvHwMacCtx = pVNic->pvHwContext;

    sig.ulPhyId = Hw11QueryOperatingPhyId(pvHwMacCtx);
    sig.ulChannel = Hw11QueryCurrentChannel(pvHwMacCtx, FALSE);
    sig.ucDefKeyMask = Hw11QueryDefaultKeyMask(pvHwMacCtx);
    
    return sig;
}

/*
    Merge signature1 with signature2
    */
VNIC_SIGNATURE
VNic11MergeSignatures(
    PVNIC_SIGNATURE pSig1,
    PVNIC_SIGNATURE pSig2
    )
{
    VNIC_SIGNATURE mergedSig = {0};
    
    ASSERT(VNic11AreCompatibleSignatures(pSig1, pSig2));

    mergedSig.ulPhyId = pSig1->ulPhyId;
    mergedSig.ulChannel = pSig1->ulChannel;
    mergedSig.ucDefKeyMask = pSig1->ucDefKeyMask | pSig2->ucDefKeyMask;
    
    return mergedSig;
}

_IRQL_requires_(DISPATCH_LEVEL)
VOID 
VNicCompleteExAccessReq(
    PVNIC pVNic,
    PVNIC_EX_ACCESS_REQ pExReq,
    BOOLEAN fCompleteReq
    )
{   
    PORT11_GENERIC_CALLBACK_FUNC pCallbkFn = pExReq->CallbkFn;
    PVOID pvCtx = pExReq->pvCtx;

    if (fCompleteReq)
    {
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Completing the exclusive access request\n", VNIC_PORT_NO));

        /*
            If we are using one of the dedicated exclusive access requests, reset them now. As
            soon as we return the call, the Port can decide to make another exclusive access
            request
            */
        if (pExReq && VNicIsPreallocatedRequest(pVNic, pExReq))
        {
            VNicInitPreAllocatedOp(pVNic);
        }
        
        // unlock before calling into the port
        _Analysis_assume_lock_held_((& pVNic->Lock)->SpinLock);
        VNicUnlock(pVNic);
        pCallbkFn(pVNic->pvPort, pvCtx);
        VNicLock(pVNic);

        MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d) Completed the exclusive access request.\n", VNIC_PORT_NO));
    }
}

VOID 
VNicCompleteResetReq(
    PVNIC pVNic,
    PRESET_REQ pResetReq,
    BOOLEAN fCompleteReq
    )
{   
    // we should not be in reset already
    ASSERT(!VNicIsInReset(pVNic));
    
    // set the reset flag so that no new operations can queue. This flag also makes sure that the
    // context does not switch out before we have completed our reset processing 
    // (i.e. VNic11Dot11ResetComplete is called)
    pVNic->fResetInProgress = TRUE;

    if (pVNic->CtxSRefCountTracker[REF_CTXS_BARRIER])
    {
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): Removing the connection start ref count\n", VNIC_PORT_NO));
        VNicRemoveCtxSRef(pVNic, REF_CTXS_BARRIER);
    }
    ASSERT(0 == pVNic->CtxSRefCountTracker[REF_CTXS_BARRIER]);

    if (fCompleteReq)
    {
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): Completing the reset request\n", VNIC_PORT_NO));

        VNicCompleteReq(pResetReq->pReq, NDIS_STATUS_SUCCESS);

        // the exclusive access is released after all reset processing is completed
    }    
}

_IRQL_requires_(DISPATCH_LEVEL)
NDIS_STATUS 
VNicCompleteChSwReq(
    PVNIC pVNic,
    PCH_SWITCH_REQ pChSwReq,
    BOOLEAN fCompleteReq
    )
{   
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    PVNIC_COMPLETION_CTX pCtx = NULL;

    do
    {
        if (VNicIsInReset(pVNic))
        {
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): VNic is in reset. Completing a pending join request with failure\n", VNIC_PORT_NO));
            ndisStatus = NDIS_STATUS_RESET_IN_PROGRESS;
            break;
        }

        ndisStatus = ALLOC_MEM(pVNic->MiniportAdapterHandle, sizeof(VNIC_COMPLETION_CTX), &pCtx);
        if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): Failed to allocate memory for a new completion context \n", VNIC_PORT_NO));
            break;
        }
        
        pCtx->CompletionFn = pChSwReq->pfnCompletionHandler;

        ndisStatus = VNicSwChHelper(
                    pVNic, 
                    pChSwReq->ulChannel, 
                    pChSwReq->ulPhyId, 
                    pChSwReq->fSwitchPhy,
                    pCtx
                    );
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): VNicSwChHelper called %!x!\n", VNIC_PORT_NO, ndisStatus));

    } while (FALSE);
    
    if (NDIS_STATUS_PENDING != ndisStatus)
    {
        if (fCompleteReq)
        {
            VNicChSwComplete(pVNic, FALSE, pChSwReq->pfnCompletionHandler, &ndisStatus);
        }
        
        if (pCtx)
        {
            FREE_MEM(pCtx);
        }
    }
    
    // always release the exclusive access we acquired for channel switch
    VNicReleaseExAccess(pVNic);

    return ndisStatus;
}

_IRQL_requires_(DISPATCH_LEVEL)
NDIS_STATUS 
VNicCompleteDefKeyReq(
    PVNIC pVNic,
    PDEF_KEY_REQ pDefKeyReq,
    BOOLEAN fCompleteReq
    )
{   
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;

    do
    {
        if (VNicIsInReset(pVNic))
        {
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): VNic is in reset. Completing a pending join request with failure\n", VNIC_PORT_NO));
            ndisStatus = NDIS_STATUS_RESET_IN_PROGRESS;
            break;
        }

        ndisStatus = VNicSetDefaultKeyHelper(
                        pVNic,
                        pDefKeyReq->MacAddr,
                        pDefKeyReq->KeyID,
                        pDefKeyReq->Persistent,
                        pDefKeyReq->AlgoId,
                        pDefKeyReq->KeyLength,
                        pDefKeyReq->KeyValue
                        );
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): VNicSetDefaultKeyHelper called %!x!\n", VNIC_PORT_NO, ndisStatus));

        ASSERT(NDIS_STATUS_PENDING != ndisStatus);
    } while (FALSE);
    
    if (NDIS_STATUS_PENDING != ndisStatus)
    {
        if (fCompleteReq)
        {
            // notify that the set default key request is complete
            NdisSetEvent(pDefKeyReq->CompletionEvent);
        }
    }

    // release the exclusive access we acquired for setting the default key
    VNicReleaseExAccess(pVNic);

    return ndisStatus;
}

_IRQL_requires_(DISPATCH_LEVEL)
NDIS_STATUS 
VNicCompleteOpPhyIdReq(
    PVNIC pVNic,
    POPERATING_PHY_ID_REQ pPhyIdReq,
    BOOLEAN fCompleteReq
    )
{   
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;

    do
    {
        if (VNicIsInReset(pVNic))
        {
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): VNic is in reset. Completing a pending join request with failure\n", VNIC_PORT_NO));
            ndisStatus = NDIS_STATUS_RESET_IN_PROGRESS;
            break;
        }

        ndisStatus = VNicSetOperatingPhyIdHelper(
                        pVNic,
                        pPhyIdReq->PhyId
                        );
        MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): VNicSetOperatingPhyIdHelper called %!x!\n", VNIC_PORT_NO, ndisStatus));

        ASSERT(NDIS_STATUS_PENDING != ndisStatus);

    } while (FALSE); 

    if (NDIS_STATUS_PENDING != ndisStatus)
    {
        if (fCompleteReq)
        {
            // notify that the set phy id request is complete
            NdisSetEvent(pPhyIdReq->CompletionEvent);
        }
    }

    // release the exclusive access we acquired for setting the operating Phy Id
    VNicReleaseExAccess(pVNic);

    return ndisStatus;
}

_IRQL_requires_(DISPATCH_LEVEL)
NDIS_STATUS 
VNicCompleteDesiredPhyIdReq(
    PVNIC pVNic,
    PDESIRED_PHY_ID_LIST_REQ pDesiredPhyIdListReq,
    BOOLEAN fCompleteReq
    )
{   
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;

    do
    {
        if (VNicIsInReset(pVNic))
        {
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): VNic is in reset. Completing a pending join request with failure\n", VNIC_PORT_NO));
            ndisStatus = NDIS_STATUS_RESET_IN_PROGRESS;
            break;
        }

        ndisStatus = VNicSetDesiredPhyIdListHelper(
                        pVNic,
                        pDesiredPhyIdListReq->PhyIDList,
                        pDesiredPhyIdListReq->PhyIDCount
                        );
        MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): VNicSetDesiredPhyIdListHelper called %!x!\n", VNIC_PORT_NO, ndisStatus));

        ASSERT(NDIS_STATUS_PENDING != ndisStatus);
        
    } while (FALSE);
    
    if (NDIS_STATUS_PENDING != ndisStatus)
    {
        if (fCompleteReq)
        {
            // notify that the set phy id request is complete
            NdisSetEvent(pDesiredPhyIdListReq->CompletionEvent);
        }        
    }

    // release the exclusive access we acquired for setting the operating Phy Id
    VNicReleaseExAccess(pVNic);

    return ndisStatus;
}


_IRQL_requires_(DISPATCH_LEVEL)
NDIS_STATUS 
VNicCompleteNicPowerStateReq(
    PVNIC pVNic,
    PNIC_POWER_STATE_REQ pNicPowerStateReq,
    BOOLEAN fCompleteReq
    )
{   
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;

    do
    {
        _Analysis_assume_lock_held_(pVNic->Lock.SpinLock);
        ndisStatus = VNicSetNicPowerStateHelper(
                        pVNic,
                        pNicPowerStateReq->PowerState,
                        pNicPowerStateReq->SelectedPhy
                        );
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d): VNicSetNicPowerStateHelper called %!x!\n", VNIC_PORT_NO, ndisStatus));

        ASSERT(NDIS_STATUS_PENDING != ndisStatus);
    } while (FALSE);
    
    if (NDIS_STATUS_PENDING != ndisStatus)
    {
        if (fCompleteReq)
        {
            // notify that the set power request is complete
            NdisSetEvent(pNicPowerStateReq->CompletionEvent);
        }
    }

    // release the exclusive access we acquired for setting the nic power state
    VNicReleaseExAccess(pVNic);

    return ndisStatus;
}


_IRQL_requires_(DISPATCH_LEVEL)
NDIS_STATUS
VNicCompleteExclusiveAccessOp(
    PVNIC pVNic,
    PENDING_OP_TYPE opType,
    PVOID pvOpData,
    BOOLEAN fCompleteReq
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;

    ASSERT(VNicOperationRequiresExAccess(opType));
    
    switch (opType)
    {
        case PENDING_OP_EX_ACCESS_REQ:
        {
            VNicCompleteExAccessReq(pVNic, pvOpData, fCompleteReq);
            break;
        }
        
        case PENDING_OP_RESET_REQ:
        {
            VNicCompleteResetReq(pVNic, pvOpData, fCompleteReq);
            break;
        }

        case PENDING_OP_CH_SW_REQ:
        {
            ndisStatus = VNicCompleteChSwReq(pVNic, pvOpData, fCompleteReq);
            break;
        }

        case PENDING_OP_DEF_KEY:
        {
            ndisStatus = VNicCompleteDefKeyReq(pVNic, pvOpData, fCompleteReq);
            break;
        }
            
        case PENDING_OP_OPERATING_PHY_ID:
        {
            ndisStatus = VNicCompleteOpPhyIdReq(pVNic, pvOpData, fCompleteReq);
            break;
        }
            
        case PENDING_OP_DESIRED_PHY_ID_LIST:
        {
            ndisStatus = VNicCompleteDesiredPhyIdReq(pVNic, pvOpData, fCompleteReq);            
            break;
        }

        case PENDING_OP_NIC_POWER_STATE:
        {
            ndisStatus = VNicCompleteNicPowerStateReq(pVNic, pvOpData, fCompleteReq);            
            break;
        }
        
        default:
        {
            ndisStatus = NDIS_STATUS_FAILURE;
            ASSERT(FALSE);
            break;
        }
    };

    return ndisStatus;
}

NDIS_STATUS
VNicHandleExAccessOp(
    PVNIC pVNic,
    PENDING_OP_TYPE opType,
    PVOID pvOpData,
    BOOLEAN fCheckForReset,
    BOOLEAN fPnPOperation
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    BOOLEAN fLocked = FALSE;
    BOOLEAN fQueueOp = FALSE;

    do
    {
        VNicLock(pVNic);
        fLocked = TRUE;

        if (fCheckForReset && VNicIsInReset(pVNic))
        {
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) is in reset. Not doing anything for the exclusive access operation \n", VNIC_PORT_NO));
            ndisStatus = NDIS_STATUS_RESET_IN_PROGRESS;
            break;
        }        

        // request the Hvl to grant us exclusive access
        ndisStatus = Hvl11RequestExAccess(pVNic->pvHvl, pVNic, fPnPOperation);
        if (NDIS_STATUS_SUCCESS == ndisStatus)
        {
            /*
                According to the HVL we already have exclusive access. However we might 
                know it yet (the context switch callback hasn't yet reached us. Regardless 
                remember our most current state
                */
            VNicSetExAccess(pVNic, TRUE);
            // having exclusive access implies being active
            VNicSetActive(pVNic, TRUE);
            
            if (VNicIsPendingOpQueueEmpty(pVNic))
            {
                MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d) already has exclusive access. Completing the operation %s ourselves.\n", VNIC_PORT_NO, GetPendingOpString(opType)));

                // we already have exclusive access. simply complete the disruptive operation
                ndisStatus = VNicCompleteExclusiveAccessOp(pVNic, opType, pvOpData, FALSE);

                // the VNicCompleteExclusiveAccessOp function will release exclusive access. 
                // transfer the responsibility                
                break;
            }
            else
            {
                /*
                    we already have exclusive access but there are still pending items in our 
                    queue. We cannot comlpete this operation yet since that might be out of
                    order. Simply queue the operation as in the case of pending exclusive access
                    */
                MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d) already has exclusive access but the queue is not empty.\n", VNIC_PORT_NO));
                
                fQueueOp = TRUE;
            }
        }
        else if (NDIS_STATUS_PENDING == ndisStatus)
        {
            fQueueOp = TRUE;
        }
        else
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): Hvl11RequestExAccess failed %!x!\n", VNIC_PORT_NO, ndisStatus));
            break;
        }
        
        ASSERT(fQueueOp);

        /*
            HVL will call us back with exclusive access or we will process it with other 
            pending items in our queue
            */
        ndisStatus = VNicQueuePendingOperation(pVNic, opType, pvOpData, fPnPOperation);
        if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d): VNicQueuePendingOperation failed %!x!\n", VNIC_PORT_NO, ndisStatus));
            break;
        }

        // we should return a pending status to our caller since the operation will be completed later
        ndisStatus = NDIS_STATUS_PENDING;
        
        MpTrace(COMP_HVL, DBG_SERIOUS, ("VNic(%d) Queued the operation %s .\n", VNIC_PORT_NO, GetPendingOpString(opType)));
    } while (FALSE);

    if (NDIS_FAILURE(ndisStatus))
    {
        if (fQueueOp)
        {
            // we wanted to queue the operation but hit a failure. Release the exclusive access
            // we requested. If fQueueOp is FALSE, we have transferred the responsibility to
            // release exclusive access
            Hvl11ReleaseExAccess(pVNic->pvHvl, pVNic);
        }
    }
    
    if (fLocked)
    {
        VNicUnlock(pVNic);
    }

    return ndisStatus;
}

VOID
VNic11Notify(
    PVNIC pVNic,
    PVOID pvNotif
)
{
    PNOTIFICATION_DATA_HEADER pNotifHdr = NULL;
    BOOLEAN fNotifyPort = FALSE;
    
    pNotifHdr = (PNOTIFICATION_DATA_HEADER)pvNotif;
    switch (pNotifHdr->Type)
    {
        case NotificationOpChannel:
        {
            POP_CHANNEL_NOTIFICATION pChNotif = (POP_CHANNEL_NOTIFICATION)pvNotif;

            VNicLock(pVNic);

            if (pChNotif->ulChannel == 0)
            {
                // invalidate the preferred channel only if this is the same VNIC whose preferred
                // channel we are using. 
                if (pVNic->pPreferredChannelSrcVNic && (pVNic->pPreferredChannelSrcVNic == pChNotif->Header.pSourceVNic))
                {
                    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) Received channel invalidation from VNic (%d). Invalidating preferred channel (%d) \n", VNIC_PORT_NO, pVNic->pPreferredChannelSrcVNic->PortNumber, pVNic->ulPreferredChannel));
                    pVNic->ulPreferredChannel = 0;
                    pVNic->pPreferredChannelSrcVNic = NULL;
                }
                else
                {
                    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) Received channel invalidation from VNic (%d). However our source VNIC is different. Ignoring update.  \n", VNIC_PORT_NO, pChNotif->Header.pSourceVNic->PortNumber));
                }
            }
            else
            {
                MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) updating preferred channel: %d ----> %d\n", VNIC_PORT_NO, pVNic->ulPreferredChannel, pChNotif->ulChannel));
                pVNic->ulPreferredChannel = pChNotif->ulChannel;
                pVNic->pPreferredChannelSrcVNic = pChNotif->Header.pSourceVNic;

                fNotifyPort = TRUE;
            }
            
            VNicUnlock(pVNic);

            // also notify the port
            if (fNotifyPort)
            {
                Port11Notify(pVNic->pvPort, pvNotif);
            }
        }
        break;

        case NotificationOpLinkState:
        case NotificationOpRateChange:
        {
            // Notify the port. We dont maintain this state
            Port11Notify(pVNic->pvPort, pvNotif);
            break;
        }

        default:
        {
            // not handled
            ASSERT(FALSE);
        }
        break;
    };
}

ULONG
VNic11QueryPreferredChannel(
    _In_  PVNIC               pVNic,
    _Out_ PBOOLEAN            pPreferredChannel
    )
{
    *pPreferredChannel = (pVNic->ulPreferredChannel != 0);
    return pVNic->ulPreferredChannel ? pVNic->ulPreferredChannel : Hw11QueryCurrentChannel(pVNic->pvHwContext, FALSE);
}

VOID
VNic11AcquireCtxSBarrier(
    PVNIC pVNic
    )
{
    BOOLEAN fProcessReqNow = FALSE;

    VNicLock(pVNic);
    
    fProcessReqNow = VNicIsActive(pVNic) && VNicIsPendingOpQueueEmpty(pVNic);
    if (fProcessReqNow)
    {
        VNicAddCtxSRef(pVNic, REF_CTXS_BARRIER);
    }
    else
    {
        VNicQueueConnectionStart(pVNic);
    }
    
    VNicUnlock(pVNic);
}

VOID
VNic11ReleaseCtxSBarrier(
    PVNIC pVNic
    )
{
    BOOLEAN fPendingConnStartDeleted = FALSE;    

    VNicLock(pVNic);

    VNicDeletePendingConnStartRequest(pVNic, &fPendingConnStartDeleted);
    if (fPendingConnStartDeleted)
    {
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): Removed a pending connection start request\n", VNIC_PORT_NO));
    }
    
    if (pVNic->CtxSRefCountTracker[REF_CTXS_BARRIER])
    {
        ASSERT(!fPendingConnStartDeleted);
        
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d): Removing the connection start ref count\n", VNIC_PORT_NO));
        VNicRemoveCtxSRef(pVNic, REF_CTXS_BARRIER);
        ASSERT(0 == pVNic->CtxSRefCountTracker[REF_CTXS_BARRIER]);
    }

    VNicUnlock(pVNic);
}

VOID
VNicSendChannelNotification(
    PVNIC pVNic,
    ULONG ulChannel
    )
{
    OP_CHANNEL_NOTIFICATION ChNotif = {0};
    
    /*
        We have successfully connected. Notify the HVL about our channel. Other 
        VNICs can use this information to optimize e.g. an AP can decide to move to
        the same channel as us
        */
    ChNotif.Header.pSourceVNic = pVNic;
    ChNotif.Header.Type = NotificationOpChannel;
    ChNotif.Header.Size = sizeof(OP_CHANNEL_NOTIFICATION);
    ChNotif.ulChannel = ulChannel;

    Hvl11Notify(pVNic->pvHvl, &ChNotif);            
}

VOID
VNicSendLinkStateNotification(
    IN  PVNIC                   pVNic,
    IN  BOOLEAN                 MediaConnected
    )
{

    OP_LINK_STATE_NOTIFICATION MediaNotif = {0};

    // We pass the notification through only if it has
    // changed
    if (pVNic->LastLinkStateNotified != MediaConnected)
    {
        pVNic->LastLinkStateNotified = MediaConnected;
        
        MediaNotif.Header.pSourceVNic = pVNic;
        MediaNotif.Header.Type = NotificationOpLinkState;
        MediaNotif.Header.Size = sizeof(OP_LINK_STATE_NOTIFICATION);
        MediaNotif.MediaConnected = MediaConnected;
        Hvl11Notify(pVNic->pvHvl, &MediaNotif);            
    }
}

Our Services

  • What our customers say about us?

© 2011-2025 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