예제 #1
0
파일: hvl_main.c 프로젝트: 340211173/Driver
VOID
HvlWaitForCtxSProcessingAndLock(_In_ PHVL pHvl)
{
    HvlLock(pHvl);
    while (HvlIsCtxSInProgress(pHvl))
    {
        HvlUnlock(pHvl);
        NdisMSleep(10 * 1000);  // 10 msec
        HvlLock(pHvl);
    }
}
예제 #2
0
파일: hvl_main.c 프로젝트: 340211173/Driver
VOID
Hvl11UnblockTimedCtxS(_Inout_ PHVL pHvl)
{
    HvlLock(pHvl);
    pHvl->ulStatusFlags &= ~HVL_TIMED_CTXS_BLOCKED;
    HvlUnlock(pHvl);
}
예제 #3
0
파일: hvl_main.c 프로젝트: 340211173/Driver
NDIS_STATUS
Hvl11DeregisterVNic(
    _In_  PHVL    pHvl,
    _In_  PVNIC   pVNic               
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;

    HvlLock(pHvl);

    HvlRemoveVNicFromCtx(pHvl, pVNic);

    // remove it from the list of VNICs that the HVL maintains
    RemoveEntryList (&pVNic->VNicLink);
    InitializeListHead(&pVNic->VNicLink);

    // if there was a cached notification sourced by this VNIC clear it now
    if (pHvl->CachedChannelNotification.Header.pSourceVNic == pVNic)
    {
        HvlClearCachedNotification(pHvl);
    }
    
    HvlUnlock(pHvl);
    
    return ndisStatus;
}
예제 #4
0
파일: hvl_main.c 프로젝트: 340211173/Driver
NDIS_STATUS
Hvl11RegisterVNic(
    _In_  PHVL    pHvl,
    _In_  PVNIC   pVNic               
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    PHVL_CONTEXT pCtx = NULL;
    
    do
    {
        HvlLock(pHvl);

        // add it to the HVL's list of VNICs
        InsertTailList(&pHvl->VNiclist, &pVNic->VNicLink);
        
        HvlAssignVNicToContext(pHvl, pVNic, &pCtx);
        
        /*
            Initially make it part of the inactive context list. It will be picked up whenever we
            next context switch to it
            */
        InsertTailList(&pHvl->InactiveContextList, &pCtx->Link);

        HvlUnlock(pHvl);

        // notify it of any existing cached notifications
        if (pHvl->CachedChannelNotification.Header.pSourceVNic)
        {
            VNic11Notify(pVNic, &pHvl->CachedChannelNotification);
        }
    } while (FALSE);

    return ndisStatus;
}
예제 #5
0
파일: hvl_main.c 프로젝트: 340211173/Driver
VOID
HvlWaitForPendingThreads(    
    _In_  PHVL pHvl
    )
{
    ULONG ulNumIter = 0;
    BOOLEAN fThreadsFinished = FALSE;

    /*
        Wait for any pending threads to complete before freeing stuff
        */
    while (!fThreadsFinished)
    {
        if ( ++ulNumIter > 30000)
        {
            // 30 seconds have passed - something must be wrong
            ASSERTMSG("Hvl pending thread count hasn't gone to 0 in last 30 seconds. Check driver state\n", FALSE);
        }
        
        HvlLock(pHvl);
        if (pHvl->ulNumThreadsPending == 0)
        {
            fThreadsFinished = TRUE;
        }
        HvlUnlock(pHvl);
        if (!fThreadsFinished)
        {
            NdisMSleep(1000);
        }
    } 
}
예제 #6
0
파일: hvl_main.c 프로젝트: 340211173/Driver
VOID
HvlNotificationsWorkItem(
    _In_ PVOID            Context,
    _In_ NDIS_HANDLE      NdisIoWorkItemHandle
    )
{
    PHVL pHvl = NULL;
    LIST_ENTRY *pEntryNotif = NULL;
    PHVL_NOTIFICATION pNotification = NULL;
    
    UNREFERENCED_PARAMETER(NdisIoWorkItemHandle);

    pHvl = (PHVL) Context;

    HvlLock(pHvl);
    
    pEntryNotif = pHvl->NotificationsQueue.Flink;
    
    while (pEntryNotif != &pHvl->NotificationsQueue)
    {
        pNotification = CONTAINING_RECORD(pEntryNotif, HVL_NOTIFICATION, Link);
        RemoveEntryList (&pNotification->Link);

        HvlNotifyAllVNics(pHvl, pNotification->pvNotif);

        pEntryNotif = pEntryNotif->Flink;
        
        HvlFreeNotification(pNotification);
    }
    
    pHvl->fNotificationsWorkItemRunning = FALSE;
    pHvl->ulNumThreadsPending--;

    HvlUnlock(pHvl);
}
예제 #7
0
파일: hvl_main.c 프로젝트: 340211173/Driver
VOID
HvlNotifyAllVNics(
    PHVL pHvl,
    PVOID pvNotif
    )
{
    LIST_ENTRY *pEntryVNic = NULL;
    PVNIC pVNic = NULL;
    PNOTIFICATION_DATA_HEADER pHdr = NULL;

    ASSERT(HvlIsLocked(pHvl));

    pHdr = (PNOTIFICATION_DATA_HEADER)pvNotif;
    
    pEntryVNic = pHvl->VNiclist.Flink;
    while (pEntryVNic != &pHvl->VNiclist)
    {
        pVNic = CONTAINING_RECORD(pEntryVNic, VNIC, VNicLink);

        // do not notify if this VNIC is the source of the notification
        if (pVNic != pHdr->pSourceVNic)
        {
            _Analysis_assume_lock_held_(pHvl->Lock.SpinLock);

            HvlUnlock(pHvl);
            VNic11Notify(pVNic, pvNotif);
            HvlLock(pHvl);
        }
        
        pEntryVNic = pEntryVNic->Flink;
    }

    // Also send the notification to the helper port
    pEntryVNic = pHvl->pHelperPortCtx->VNicList.Flink;
    pVNic = CONTAINING_RECORD(pEntryVNic, VNIC, CtxLink);

    _Analysis_assume_lock_held_(pHvl->Lock.SpinLock);
    HvlUnlock(pHvl);
    VNic11Notify(pVNic, pvNotif);
    HvlLock(pHvl);
}
예제 #8
0
파일: hvl_main.c 프로젝트: 340211173/Driver
VOID
Hvl11Notify(
    PHVL                    pHvl,
    PVOID                   pvNotif
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;    
    PNOTIFICATION_DATA_HEADER pHdr = NULL;

    pHdr = (PNOTIFICATION_DATA_HEADER)pvNotif;
    
    HvlLock(pHvl);

    do
    {
        // update our internal state
        HvlCacheNotification(pHvl, pvNotif);

        ndisStatus = HvlQueueNotification(pHvl, pvNotif);
        if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("HvlQueueNotification failed %!x!\n", ndisStatus));
            break;
        }

        if (!pHvl->fNotificationsWorkItemRunning)
        {
            pHvl->fNotificationsWorkItemRunning = TRUE;
            pHvl->ulNumThreadsPending++;
            
            NdisQueueIoWorkItem(
                pHvl->NotificationsWorkItemHandle,
                HvlNotificationsWorkItem,
                pHvl
                );
        }
        else
        {
            // the work item is already running. It will take care of processing this notification
        }
    } while (FALSE);
    
    HvlUnlock(pHvl);
}
예제 #9
0
파일: hvl_main.c 프로젝트: 340211173/Driver
NDIS_STATUS
Hvl11DeregisterHelperPort(
    _In_  PHVL    pHvl,
    _In_  PVNIC   pVNic
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;

    UNREFERENCED_PARAMETER(pVNic);

    ASSERT(HvlIsHelperVNic(pHvl, pVNic));
    
    HvlLock(pHvl);

    HvlRemoveVNicFromCtx(pHvl, pVNic);

    HvlUnlock(pHvl);
    
    return ndisStatus;
}
예제 #10
0
파일: hvl_main.c 프로젝트: 340211173/Driver
NDIS_STATUS
Hvl11RegisterHelperPort(
    _In_  PHVL    pHvl,
    _In_  PVNIC   pVNic               
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    PHVL_CONTEXT pCtx = NULL;

    do
    {
        HvlLock(pHvl);

        HvlAssignVNicToContext(pHvl, pVNic, &pCtx);
        pHvl->pHelperPortCtx = pCtx;
        
        HvlUnlock(pHvl);
    } while (FALSE);

    return ndisStatus;
}
예제 #11
0
파일: hvl_main.c 프로젝트: 340211173/Driver
VOID
Hvl11EnableContextSwitches(
    _In_  PHVL                    pHvl
    )
{
    HvlLock(pHvl);

    if (!pHvl->fVirtualizationEnabled)
    {
        pHvl->fVirtualizationEnabled = TRUE;

        NdisQueueIoWorkItem(
            pHvl->CtxSWorkItemHandle,
            HvlCtxSWorkItem,
            pHvl
            );

        // run the context switch logic for the first time
        KeSetEvent(&pHvl->CtxSEvent, 0, FALSE);
    }
    
    HvlUnlock(pHvl);
}
예제 #12
0
VOID
HvlNotifyAllVNicsInContext(
    PHVL            pHvl,
    PHVL_CONTEXT    pCtx,
    PVNIC_FUNCTION  pVnicFn,
    ULONG           ulFlags
    )
{
    LIST_ENTRY *pEntryVNic = NULL;
    PVNIC pVNic = NULL;

    _Analysis_assume_lock_held_(pHvl->Lock.SpinLock);

    HvlUnlock(pHvl);
    
    pEntryVNic = pCtx->VNicList.Flink;
    while (pEntryVNic != &pCtx->VNicList)
    {
        pVNic = CONTAINING_RECORD(pEntryVNic, VNIC, CtxLink);
        pVnicFn(pVNic, ulFlags);
        pEntryVNic = pEntryVNic->Flink;
    }
    HvlLock(pHvl);
}
예제 #13
0
파일: hvl_main.c 프로젝트: 340211173/Driver
VOID
HvlCtxSProcessing(
    PHVL pHvl,
    PHVL_CONTEXT pNextActiveContext,
    BOOLEAN fMerged,
    ULONG        ulFlags
    )
{
    //NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    PHVL_CONTEXT pCurrCtx = NULL, pNextCtx = NULL;
    BOOLEAN fSameCtx = FALSE;
    LIST_ENTRY *pEntryVNic = NULL;
    PVNIC pVNic = NULL;
    PVNIC pHelperVNic = NULL;
    LIST_ENTRY *pHelperEntry = NULL;

    ASSERT(HvlIsLocked(pHvl));

    // we can have only one instance of this routine running
    ASSERT(!HvlIsCtxSInProgress(pHvl));

    HvlSetCtxSInProgress(pHvl);
    
    do
    {        
        // get the current active context
        pCurrCtx = pHvl->pActiveContext;

        if (NULL == pNextActiveContext)
        {
            // Use our logic to find the next active context
            pNextCtx = HvlFindNextCtx(pHvl);
        }
        else
        {
            // the caller specified a particular context to switch to
            pNextCtx = pNextActiveContext;
        }
        
        if (pCurrCtx && pNextCtx == pCurrCtx)
        {
            if (!fMerged)
            {
                /*
                    No context switch needs to happen.
                    */
                MpTrace(COMP_HVL, DBG_NORMAL, ("The next context (%p) is the same as the current one. Not doing a context switch\n", pNextCtx));
                #if DBG
                    pEntryVNic = pNextCtx->VNicList.Flink;
                    while (pEntryVNic != &pNextCtx->VNicList)
                    {
                        pVNic = CONTAINING_RECORD(pEntryVNic, VNIC, CtxLink);
                        ASSERT(pVNic->fActive);
                        pEntryVNic = pEntryVNic->Flink;
                    }
                #endif                
                break;
            }
            else
            {
                MpTrace(COMP_HVL, DBG_NORMAL, ("The next context (%p) is the same as the current one. But we have just done a merge. Continuing with the context switch\n", pNextCtx));
                /*
                    remembe that our current and next contexts are the same. We need to do 
                    this so that we do not update the current context below
                    */
                fSameCtx = TRUE;
            }
        }

        if (NULL == pNextCtx)
        {
            /*
                No context switch needs to happen.
                */
            MpTrace(COMP_HVL, DBG_NORMAL, ("No contexts exist. Not doing a context switch\n"));
            break;
        }

        /*
            Now update the Hvl's state about which VNIC is loosing exclusive access. It
            is important to update this before VNic11CtxSFromVNic call has been made. This 
            ensures that the VNIC does not gain access while it is being moved away from
            active context
            */
        if (VNIC_FLAG_HVL_ACTIVATED & ulFlags)
        {
            // this is an activation by the helper port

            ASSERT(pNextActiveContext);
            ASSERT(pNextActiveContext->ulNumVNics == 1);

            if (NULL == pNextActiveContext)
            {
                return;
            }

            pEntryVNic = pNextActiveContext->VNicList.Flink;
            pVNic = CONTAINING_RECORD(pEntryVNic, VNIC, CtxLink);

            pHelperEntry = pHvl->pHelperPortCtx->VNicList.Flink;
            pHelperVNic = CONTAINING_RECORD(pHelperEntry, VNIC, CtxLink);    

            // Helper port is delegating exclusive access to someone else. Take away the delegated access
            if (pHvl->pExAccessDelegatedVNic && pVNic != pHvl->pExAccessDelegatedVNic )
            {
                pHvl->pExAccessDelegatedVNic = NULL;
            }
        }

        // inform the active VNICs that they are going to be become inactive

        if (NULL != pCurrCtx)
        {
            HvlNotifyAllVNicsInContext(pHvl, pCurrCtx, VNic11CtxSFromVNic, 0);
        }
        
        /*
            Tell the hardware layer that we are now going to context switch. Note that this has
            to happen only after the VNICs have been told they are becoming inactive. The
            reason for this is that receives should continue to happen while the VNICs perform
            the tasks they need to become inactive (e.g. send PS packet to the AP)

            Also note that we need to give up our lock before we call into the hardware. This is
            because the Hw might need to wait for some operations to complete before it can
            return this call. Since the context switch processing runs in a single thread, we can
            safely leave the lock and re-acquire it
            */
        HvlUnlock(pHvl);                    
        Hw11CtxSStart(pHvl->Hw);
        HvlLock(pHvl);

        // ask the VNICs in the new context to program the hardware
        HvlNotifyAllVNicsInContext(pHvl, pNextCtx, VNic11ProgramHw, 0);

        /*
            program the hardware for the new context - this programs any settings that need
            to be merged across the VNICs
            */
        HvlProgramHw(pHvl, pNextCtx);        

        /*
            Now update the Hvl's state about which VNIC is active or has exclusive access. It
            is important to update this after the VNic11ProgramHw and HvlProgramHw calls
            have been made. This ensures that the VNIC does not gain access prematurely
            */
        if (VNIC_FLAG_HVL_ACTIVATED & ulFlags)
        {
            // this is an activation by the helper port

            // pHelperEntry and pHelperVNic must have been already calculated above
            ASSERT(pHelperEntry && pHelperVNic);
                    
            if (pVNic != pHelperVNic)
            {
                // Some VNIC is being delegated exclusive access
                pHvl->pExAccessDelegatedVNic = pVNic;
            }    
        }
        else if (VNIC_FLAG_GRANTED_EX_ACCESS & ulFlags)
        {
            // a VNIC is being given exclusive access. Update our state to reflect this
            ASSERT(pNextActiveContext);
            ASSERT(pNextActiveContext->ulNumVNics == 1);

            if (NULL == pNextActiveContext)
            {
                return;
            }

            pEntryVNic = pNextActiveContext->VNicList.Flink;
            pVNic = CONTAINING_RECORD(pEntryVNic, VNIC, CtxLink);
            
            HvlGrantExAccess(pHvl, pVNic);
        }
        
        if (!fSameCtx)
        {
            HvlUpdateActiveCtx(pHvl, pCurrCtx, pNextCtx);
            MpTrace(COMP_HVL, DBG_NORMAL, ("Current context = %p, next context = %p, fSameCtx = %s\n", pCurrCtx, pNextCtx, fSameCtx?"TRUE":"FALSE"));
        }
        
        // tell the hardware that we are ready with the new context
        HvlUnlock(pHvl);                    
        Hw11CtxSComplete(pHvl->Hw);
        HvlLock(pHvl);

        // inform the VNICs that they are now active
        HvlNotifyAllVNicsInContext(pHvl, pNextCtx, VNic11CtxSToVNic, ulFlags);
    } while (FALSE);

    HvlCtxSDone(pHvl);

    return;
}
예제 #14
0
파일: hvl_main.c 프로젝트: 340211173/Driver
NDIS_STATUS
Hvl11ReleaseExAccess(PHVL pHvl, PVNIC pVNic)
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    PVNIC pExVNic = NULL;
    PHVL_EX_ACCESS_REQ pExReq = NULL;
    
    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) releasing exclusive access\n", VNIC_PORT_NO));

    HvlLock(pHvl);

    pExReq = HvlDequeueNextReqForVNic(pHvl, pVNic);
    
    if (pExReq && 0 == pExReq->ulRefCount)
    {
        /*
            This is the last release by this VNIC. Actually take away exclusive access if this is 
            the current owner of exclusive access
            */
        pExVNic = HvlGetExAccessVNic(pHvl);
        
        if (pExVNic == pVNic)
        {
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) currently owns exclusive access. Giving it up and removing it from the queue\n", VNIC_PORT_NO));
            
            HvlReleaseExAccess(pHvl, pVNic);    

            // set the event so that some other VNIC waiting for access can now get it
            KeSetEvent(&pHvl->ExAccessEvent, 0, FALSE);   
        }
        else
        {
            /*
                The VNIC is releasing exclusive access even when it doesn't own it. This can
                happen - take for example the following
                1. There is a helper port and an extensible station port
                2. A Pause is happening and the helper port has exclusive access.
                3. The station asks its VNIC to perform a dot11 reset
                4. The station's VNIC requests exclusive access. Since the helper port has it, its request is queued
                5. The helper port activates the station as part of its Pause processing
                6. The station VNIC's CtxSToVNic is called and the VNIC learns it has exclusive access
                7. The station VNIC completes its reset operation and asks Hvl to release exclusive access
                8. The calls comes into the HVL and we would land up in this else clause.

                The correct thing to do here is to simply remove the station VNIC's request from
                the queue which is what we have done here
                */
            MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) doesn't own exclusive access currently. Just removing it from the queue\n", VNIC_PORT_NO));

            if (NULL == pExVNic)
            {
                //no one has exclusive access right now. set the event so that some other 
                //VNIC waiting for access can now get it
                KeSetEvent(&pHvl->ExAccessEvent, 0, FALSE);
            }
        }

        // delete the request
        if (HvlIsPreAllocatedRequest(pHvl, pExReq))
        {
            // it is a pre-allocated request - simply reset it
            HvlInitPreAllocatedOp(pHvl);
        }
        else
        {
            HvlDeleteExAccessRequest(pHvl, pExReq);        
        }
    }
    else
    {
        ASSERT(!HvlIsHelperVNic(pHvl, pVNic));
        
        /*
            This VNIC has more requests for exclusive access pending
            */
        MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) has %d more exclusive access requests. Decremented the refcount. Not removing it from the queue\n", VNIC_PORT_NO, pExReq ? pExReq->ulRefCount : -1));
        ndisStatus = NDIS_STATUS_PENDING;        
    }
    
    HvlUnlock(pHvl);

    return ndisStatus;
}
예제 #15
0
파일: hvl_main.c 프로젝트: 340211173/Driver
NDIS_STATUS
Hvl11RequestExAccess(
    _In_ PHVL pHvl, 
    _In_ PVNIC pVNic,
    _In_ BOOLEAN fPnPOperation
    )
{
    NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS;
    BOOLEAN fLocked = FALSE;
    
    do
    {
        HvlLock(pHvl);
        fLocked = TRUE;
        
        ndisStatus = HvlQueueExAccessRequest(pHvl, pVNic, fPnPOperation);
        if (NDIS_STATUS_SUCCESS != ndisStatus)
        {
            MpTrace(COMP_HVL, DBG_SERIOUS, ("HvlQueueExAccessRequest failed 0x%x", ndisStatus));
            break;
        }

        // now determine the return status to be given to the VNIC. 
        if (pHvl->pExAccessDelegatedVNic)
        {            
            // if the helper port has delegated exclusive access to some VNIC, new request 
            // by only that VNIC can succeed. Everyone else, including the helper port, should
            // wait
            if (pHvl->pExAccessDelegatedVNic == pVNic)
            {
                MpTrace(COMP_HVL, DBG_NORMAL, ("VNic (%d) has already been delegated exclusive access. Completing its request for exclusive access inline. \n", VNIC_PORT_NO));
                ndisStatus = NDIS_STATUS_SUCCESS;
            }
            else
            {
                ndisStatus = NDIS_STATUS_PENDING;
                MpTrace(COMP_HVL, DBG_NORMAL, ("VNic (%d) has been delegated exclusive access. Pending VNic(%d)'s request for exclusive access.\n", pHvl->pExAccessDelegatedVNic->PortNumber, VNIC_PORT_NO));
            }
        }
        else
        {
            if (HvlGetExAccessVNic(pHvl) == pVNic)
            {
                if (HvlIsHelperVNic(pHvl, pVNic))
                {
                    // helper port never gets exclusive access inline
                    MpTrace(COMP_HVL, DBG_NORMAL, ("Helper VNic (%d) has exclusive access. Still pending VNic(%d)'s exclusive access request \n", pHvl->pExAccessVNic->PortNumber, VNIC_PORT_NO));
                    ndisStatus = NDIS_STATUS_PENDING;
                }
                else
                {
                    // let the VNIC know that it already has exclusive access
                    MpTrace(COMP_HVL, DBG_NORMAL, ("VNic (%d) already has exclusive access. Completing its request for exclusive access inline. \n", VNIC_PORT_NO));
                    ndisStatus = NDIS_STATUS_SUCCESS;
                }
            }
            else if (HvlGetExAccessVNic(pHvl))
            {
                // the VNIC will get exclusive access later
                MpTrace(COMP_HVL, DBG_NORMAL, ("VNic (%d) has exclusive access. Pending VNic(%d)'s exclusive access request \n", pHvl->pExAccessVNic->PortNumber, VNIC_PORT_NO));
                ndisStatus = NDIS_STATUS_PENDING;
            }
            else
            {
                // the VNIC will get exclusive access later
                MpTrace(COMP_HVL, DBG_NORMAL, ("No VNic has exclusive access. Pending VNic(%d)'s exclusive access request \n", VNIC_PORT_NO));
                ndisStatus = NDIS_STATUS_PENDING;
            }
        }
    } while (FALSE);

    if (fLocked)
    {
        HvlUnlock(pHvl);
    }
    
    if (NDIS_STATUS_PENDING == ndisStatus)
    {
        KeSetEvent(&pHvl->ExAccessEvent, 0, FALSE);
    }
    
    return ndisStatus;
}
예제 #16
0
파일: hvl_main.c 프로젝트: 340211173/Driver
VOID
HvlCtxSWorkItem(
    PVOID            Context,
    NDIS_HANDLE      NdisIoWorkItemHandle
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PHVL pHvl = NULL;
    LARGE_INTEGER waitTime = {0};
    #define NUM_EVENTS  3
    PVOID EventArray[NUM_EVENTS];  
    BOOLEAN fTerminating = FALSE;
    
    UNREFERENCED_PARAMETER(NdisIoWorkItemHandle);

    if (NULL == Context)
    {
        MPASSERT(Context != NULL);
        return;
    }

    pHvl = (PHVL) Context;
    EventArray[0] = &pHvl->TerminatingEvent;
    EventArray[1] = &pHvl->CtxSEvent;
    EventArray[2] = &pHvl->ExAccessEvent;

    /*
        The waitTime should ideally be a function of the beacon period. For now 
        assume a constant
        */
    waitTime.QuadPart = HVL_DEFAULT_CONTEXT_SWITCH_PARK_TIME_MSEC * -1;
    
    do
    {
        ntStatus = KeWaitForMultipleObjects(NUM_EVENTS, EventArray, WaitAny, Executive, KernelMode, FALSE, &waitTime, NULL);
        switch (ntStatus)
        {
            case STATUS_TIMEOUT:
                // timed context switch processing. 
                HvlProcessTimedCtxSwitch(pHvl);

                // optionally set a different waitTime for the next wait
                break;

            case STATUS_WAIT_0:
                // we are terminating. Time to get rid of this work item processing
                HvlLock(pHvl);
                pHvl->ulNumThreadsPending--;
                HvlUnlock(pHvl);  
                fTerminating = TRUE;
                break;

            case STATUS_WAIT_1:
                // we are asked to do context switch processing
                HvlProcessTimedCtxSwitch(pHvl);
                break;

            case STATUS_WAIT_2:
                // we have a new request for exclusive access
                HvlProcessExAccessReq(pHvl);
                break;
                
            default:
                MpTrace(COMP_HVL, DBG_SERIOUS, ("KeWaitForMultipleObjects returned error 0x%x", ntStatus));
                break;
        }        
    } while (!fTerminating);
}