VOID HelperPortCompleteScanProcess( _In_ PMP_HELPER_PORT HelperPort, _In_ PMP_SCAN_PARAMETERS ScanParameters, _In_ PNDIS_STATUS CompletionStatus ) { MpTrace(COMP_SCAN, DBG_NORMAL, ("Completed the scan process of %p for port %p\n", ScanParameters, ScanParameters->RequestingPort)); MP_ACQUIRE_PORT_LOCK(HELPPORT_GET_MP_PORT(HelperPort), FALSE); // Clear the active pointer MPASSERT(ScanParameters == HelperPort->ScanContext.ActiveScanParameters); HelperPort->ScanContext.ActiveScanParameters = NULL; ScanParameters->State = SCAN_REQUEST_IN_USE; MP_RELEASE_PORT_LOCK(HELPPORT_GET_MP_PORT(HelperPort), FALSE); // Now perform the indication HelperPortIndicateScanCompletion(HelperPort, ScanParameters, CompletionStatus); // We reacquire the lock to ensure that the cancel does not conflict // with this completion MP_ACQUIRE_PORT_LOCK(HELPPORT_GET_MP_PORT(HelperPort), FALSE); // The scan is done ScanParameters->State = SCAN_COMPLETED; HelperPortScanParametersReleaseRef(HelperPort, ScanParameters); MP_RELEASE_PORT_LOCK(HELPPORT_GET_MP_PORT(HelperPort), FALSE); // If another scan is queued, run it now HelperPortProcessPendingScans(HelperPort); }
// First disable interrupts and then deregister interrupts VOID Hw11Stop( __in PHW Hw, __in NDIS_HALT_ACTION HaltAction ) { UNREFERENCED_PARAMETER(HaltAction); HW_ACQUIRE_HARDWARE_LOCK(Hw, FALSE); HW_SET_ADAPTER_STATUS(Hw, HW_ADAPTER_HALTING); HW_RELEASE_HARDWARE_LOCK(Hw, FALSE); // // Deregister interrupts. We must disable them before deregistering interrupts // // MpTrace(COMP_TESTING, DBG_SERIOUS, ("Hw11Stop \n")); HwDisableInterrupt(Hw, HW_ISR_TRACKING_HWSTOP); HwDeregisterInterrupt(Hw); // There must be no pending operations at this time MPASSERT(Hw->AsyncFuncRef == 0); // // Ensure that we have stop beaconing // Hw->MacState.BeaconEnabled = FALSE; Hw->MacState.BSSStarted = FALSE; // // Flush all the MSDU in the reassembly line // HwFlushMSDUReassemblyLine(Hw); // Stop everything in the H/W (We may restart things for context switches, etc) HalStop(Hw->Hal); }
// // Reset Step 1 - Cleanup any "pending" operations // VOID Hw11NdisResetStep1( __in PHW Hw ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; // if we are being reset because our sends are hung, we do not want to assert. however // if sends are not the cause, we do want to assert. if (!Hw11ArePktsPending(Hw)) { MPASSERT(FALSE); } // // Set state as in reset // HW_ACQUIRE_HARDWARE_LOCK(Hw, FALSE); HW_SET_ADAPTER_STATUS(Hw, HW_ADAPTER_IN_RESET); HW_RELEASE_HARDWARE_LOCK(Hw, FALSE); // // Now cleanup everything // // Cancel scan (if it is running) HwCancelScan(Hw); // Wait for pending operations in the hardware to finish HW_WAIT_FOR_ACTIVE_OPERATIONS_TO_FINISH(Hw); // Wait for active sends to be finish HW_WAIT_FOR_ACTIVE_SENDS_TO_FINISH(Hw); // Disable interrupts HwDisableInterrupt(Hw, HW_ISR_TRACKING_NDIS_RESET); ndisStatus = HalResetStart(Hw->Hal); MPASSERT(ndisStatus == NDIS_STATUS_SUCCESS); HalStop(Hw->Hal); HwFlushSendEngine(Hw, FALSE); // Dont wait for pending receives here. They may be stuck in protocols on // sends & the sends may be queued in the ports. That would cause a deadlock }
VOID MPReturnNetBufferLists( NDIS_HANDLE MiniportAdapterContext, PNET_BUFFER_LIST NetBufferLists, ULONG ReturnFlags ) { PADAPTER adapter = (PADAPTER)MiniportAdapterContext; PMP_PORT destinationPort = NULL; PNET_BUFFER_LIST currentNetBufferList = NetBufferLists, nextNetBufferList; // // We increment the port list refcount. This would avoid a pause from finishing // while we are processing this NBL and that ensures that the port list does not // change // MP_INCREMENT_PORTLIST_REFCOUNT(adapter); // // Walk the chain of netbuffer lists // while (currentNetBufferList != NULL) { // // Currently we return 1 NET_BUFFER_LIST at a time since we may be getting // back NET_BUFFER_LISTS indicated by different ports on 1 call. // This can be optimized // nextNetBufferList = NET_BUFFER_LIST_NEXT_NBL(currentNetBufferList); NET_BUFFER_LIST_NEXT_NBL(currentNetBufferList) = NULL; destinationPort = MP_NBL_SOURCE_PORT(currentNetBufferList); MPASSERT(destinationPort != NULL); // // Pass it to the appropriate port for processing // Port11HandleReturnNetBufferLists( destinationPort, currentNetBufferList, ReturnFlags ); currentNetBufferList = nextNetBufferList; } // // We were protecting the port list only until we hand it to the port. After this point, the port // is responsible for ensuring that it does not let the port get deleted while // it has packets pending on it // MP_DECREMENT_PORTLIST_REFCOUNT(adapter); }
VOID HelperPortTerminateBSSList( __in PMP_HELPER_PORT HelperPort ) { PMP_BSS_LIST BSSList = &(HelperPort->BSSList); MPASSERT(IsListEmpty(&(BSSList->List))); MP_FREE_READ_WRITE_LOCK(&(BSSList->ListLock));
VOID Hw11Terminate( __in PHW Hw ) { // // Deregister the DMA from NDIS // if (Hw->MiniportDmaHandle != NULL) { NdisMDeregisterScatterGatherDma(Hw->MiniportDmaHandle); } // // Cancel all other timers (these are all stopped already) // MPASSERT(NdisCancelTimerObject(Hw->ScanContext.Timer_Scan) == FALSE); MPASSERT(NdisCancelTimerObject(Hw->PhyState.Timer_Doze) == FALSE); MPASSERT(NdisCancelTimerObject(Hw->PhyState.Timer_Awake) == FALSE); }
VOID MPCancelSendNetBufferLists( NDIS_HANDLE MiniportAdapterContext, PVOID CancelId ) { // TODO: Unsure how to identify which port the packets have been // sent on UNREFERENCED_PARAMETER(MiniportAdapterContext); UNREFERENCED_PARAMETER(CancelId); MPASSERT(FALSE); }
VOID HelperPortNotify( _In_ PMP_PORT Port, PVOID Notif ) { PMP_HELPER_PORT helperPort = MP_GET_HELPPORT(Port); PNOTIFICATION_DATA_HEADER notifyHeader = (PNOTIFICATION_DATA_HEADER)Notif; switch (notifyHeader->Type) { case NotificationOpLinkState: { POP_LINK_STATE_NOTIFICATION pMediaNotif = (POP_LINK_STATE_NOTIFICATION)Notif; MP_ACQUIRE_PORT_LOCK(Port, FALSE); if (pMediaNotif->MediaConnected) { MpTrace(COMP_SCAN, DBG_NORMAL, ("Incrementing connect count for CONNECT. current = %d\n", helperPort->ScanContext.MediaConnectedCount)); MPASSERT(helperPort->ScanContext.MediaConnectedCount < MP_MAX_NUMBER_OF_PORT); helperPort->ScanContext.MediaConnectedCount++; } else { MpTrace(COMP_SCAN, DBG_NORMAL, ("Decrementing connect count for DISCONNECT. current = %d\n", helperPort->ScanContext.MediaConnectedCount)); MPASSERT(helperPort->ScanContext.MediaConnectedCount > 0); helperPort->ScanContext.MediaConnectedCount--; } MP_RELEASE_PORT_LOCK(Port, FALSE); break; } default: break; } }
#if DBG NDIS_STATUS HelperPortCheckForExtraRef( __in PMP_HELPER_PORT HelperPort ) { MP_RW_LOCK_STATE LockState; PLIST_ENTRY pListEntry; PMP_BSS_ENTRY pBSSEntry = NULL; PMP_BSS_LIST pDiscoveredBSSList = &(HelperPort->BSSList); LIST_ENTRY TempList; // // Entries that are currently in use (eg for connection) // we cannot flush and instead would put in the temporary queue // InitializeListHead(&TempList); MP_ACQUIRE_WRITE_LOCK(&(HelperPort->BSSList.ListLock), &LockState); while (!IsListEmpty(&(pDiscoveredBSSList->List))) { pListEntry = RemoveHeadList(&(pDiscoveredBSSList->List)); pBSSEntry = CONTAINING_RECORD(pListEntry, MP_BSS_ENTRY, Link); // // Check that the BSS entry does not have an extra ref count // MPASSERT(pBSSEntry->RefCount <= 1); InsertTailList(&TempList, pListEntry); } pDiscoveredBSSList->NumOfBSSEntries = 0; // // Restore entries that are in use // while (!IsListEmpty(&TempList)) { pListEntry = RemoveHeadList(&TempList); InsertTailList(&(pDiscoveredBSSList->List), pListEntry); pDiscoveredBSSList->NumOfBSSEntries++; } MP_RELEASE_WRITE_LOCK(&(HelperPort->BSSList.ListLock), &LockState); return NDIS_STATUS_SUCCESS;
NDIS_STATUS HwSetChannel( _In_ PHW Hw, _In_ ULONG PhyId, _In_ UCHAR Channel ) { // Must only be called for the active phy MPASSERT(PhyId == Hw->PhyState.OperatingPhyId); // When setting the channel, we dont check if we are not already on that // channel. This is because this may be called after setting a PhyID and // that does not necessarily set the channel HW_ACQUIRE_HARDWARE_LOCK(Hw, FALSE); HW_SET_ADAPTER_STATUS(Hw, HW_ADAPTER_IN_CHANNEL_SWITCH); HW_RELEASE_HARDWARE_LOCK(Hw, FALSE); // Wait for active send threads to finish. We dont wait // for anything else on an HAL reset since some of those // operations themselves may be causing the reset (Eg. channel // switch of a scan) HW_WAIT_FOR_ACTIVE_SENDS_TO_FINISH(Hw); // MpTrace(COMP_TESTING, DBG_SERIOUS, ("HwSetChannel \n")); HwDisableInterrupt(Hw, HW_ISR_TRACKING_CHANNEL); // Flush the sends HwFlushSendEngine(Hw, FALSE); HalSwitchChannel(Hw->Hal, PhyId, Channel, FALSE ); HwResetSendEngine(Hw, FALSE); HwResetReceiveEngine(Hw, FALSE); HalStartReceive(Hw->Hal); // MpTrace(COMP_TESTING, DBG_SERIOUS, ("HwSetChannel \n")); HwEnableInterrupt(Hw, HW_ISR_TRACKING_CHANNEL); HW_CLEAR_ADAPTER_STATUS(Hw, HW_ADAPTER_IN_CHANNEL_SWITCH); return NDIS_STATUS_SUCCESS; }
VOID HelperPortTerminateScanContext( _In_ PMP_HELPER_PORT HelperPort ) { PMP_SCAN_PARAMETERS scanParameters = NULL; ULONG i; // There can be scan requests around if we were waiting for exclusive access // for a request and it did not get satisfied if (HelperPort->ScanContext.ParametersCount != 0) { // We may have scan requests structures allocated, waiting for exclusive access // but that is never going to get satisfied. Free the requests for (i = 0; i < MP_MAX_NUMBER_OF_PORT; i++) { scanParameters = &HelperPort->ScanContext.ScanParameters[i]; if (scanParameters->State != SCAN_EMPTY_REQUEST) { // The only condition in which this is OK is when we are waiting for // an exclusive access MPASSERT(scanParameters->UsageCount == 1); HelperPortScanParametersReleaseRef(HelperPort, scanParameters); } } } if (HelperPort->ScanContext.Timer_Scan) { NdisFreeTimerObject(HelperPort->ScanContext.Timer_Scan); HelperPort->ScanContext.Timer_Scan = NULL; } for (i = 0; i < HW11_MAX_PHY_COUNT; i++) { if (HelperPort->ScanContext.ScanChannels[i].ChannelList != NULL) { MP_FREE_MEMORY(HelperPort->ScanContext.ScanChannels[i].ChannelList); HelperPort->ScanContext.ScanChannels[i].ChannelList = NULL; } } }
/* NOTE This packet gives up the VNIC lock before calling into the hardware. The VNIC state might get changed during the course of the call */ VOID VNicSendPktsToHw( _In_ PVNIC pVNic, _In_ ULONG ulNumPkts, _In_ PMP_TX_MSDU PacketList, _In_ ULONG SendFlags ) { BOOLEAN fDispatchLevel = SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? TRUE : FALSE; PMP_TX_MSDU currentPacket = NULL; ULONG myCount = 0; currentPacket = PacketList; while (currentPacket != NULL) { myCount++; currentPacket = MP_TX_MSDU_NEXT_MSDU(currentPacket); } MPASSERT(myCount == ulNumPkts); ASSERT(VNicIsLocked(pVNic)); ASSERT(VNicIsActive(pVNic)); MpTrace(COMP_HVL, DBG_LOUD, ("VNic(%d): Sending %d packets to the hardware \n", VNIC_PORT_NO, ulNumPkts)); /* We can call the hardware for sending packets. We need to increment the context switch ref count to avoid becoming inactive. The context switch ref count will be decremented when we receive the send completionAlso we need to give up our lock before calling the hardware. */ VNicIncCtxSRef(pVNic, ulNumPkts, REF_SEND_PKTS); // we also need to keep track of the sends outstanding to the hardware VNicIncOutstandingSends(pVNic, ulNumPkts); _Analysis_assume_lock_held_((& pVNic->Lock)->SpinLock); VNicUnlockAtDispatch(pVNic, fDispatchLevel); Hw11SendPackets(pVNic->pvHwContext, PacketList, SendFlags); VNicLockAtDispatch(pVNic, fDispatchLevel); }
VOID HwPhyProgramWorkItem( PVOID Context, NDIS_HANDLE NdisIoWorkItemHandle ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PHW hw = (PHW)Context; HW_GENERIC_CALLBACK_FUNC completionHandler = NULL; PHW_PHY_CONTEXT newPhyContext = NULL; PHW_MAC_CONTEXT completeContext = NULL; if (NULL == hw) { MPASSERT(hw); return; } newPhyContext = hw->PhyState.NewPhyContext; UNREFERENCED_PARAMETER(NdisIoWorkItemHandle); ndisStatus = HwSetPhyContext(hw, hw->PhyState.DestinationPhyId, newPhyContext ); // Complete the phy programming completionHandler = hw->PhyState.PendingPhyProgramCallback; completeContext = hw->PhyState.PhyProgramMacContext; hw->PhyState.PendingPhyProgramCallback = NULL; // Remove the extra ref. We do this before the workitem call since otherwise // if from the completion handler context the upper layer calls back into us, we // could deadlock (Hw11CtxSStart is one function that can deadlock) HW_DECREMENT_ACTIVE_OPERATION_REF(hw); // Invoke the completion handler completionHandler(hw, completeContext, &ndisStatus); }
VOID Sta11TerminatePort( _In_ PMP_PORT Port ) { PMP_EXTSTA_PORT extStaPort = MP_GET_STA_PORT(Port); // // We should have been paused and have already // stopped periodic scanning. Ensure that that is the case // MPASSERT(STA_TEST_SCAN_FLAG(extStaPort, STA_STOP_PERIODIC_SCAN)); // // Cleanup all the connection state // StaResetConnection(extStaPort, FALSE); // Cleanup station configuration StaInitializeStationConfig(extStaPort); // Cleanup the AdHoc station list StaResetAdHocStaInfo(extStaPort, TRUE); }
NDIS_STATUS MPReset( NDIS_HANDLE MiniportAdapterContext, PBOOLEAN AddressingReset ) { PADAPTER adapter = (PADAPTER)MiniportAdapterContext; NDIS_STATUS ndisStatus = NDIS_STATUS_PENDING; NDIS_HANDLE workitemHandle; *AddressingReset = TRUE; #if DBG if (adapter->Debug_BreakOnReset) { DbgPrint("Received NdisReset\n"); DbgBreakPoint(); } #endif do { // // Set the flag so that other routines stop proceeding // MP_SET_ADAPTER_STATUS(adapter, MP_ADAPTER_IN_RESET); // // If our halt handler has been called, we should not reset // if (MP_TEST_ADAPTER_STATUS(adapter, MP_ADAPTER_HALTING)) { MPASSERT(FALSE); // Would be an interesting scenario to investigate ndisStatus = NDIS_STATUS_SUCCESS; break; } // // Handle the reset asynchronously since we can be called at either dispatch // or passive IRQL // workitemHandle = NdisAllocateIoWorkItem(adapter->MiniportAdapterHandle); if(workitemHandle == NULL) { MpTrace(COMP_INIT_PNP, DBG_SERIOUS, ("Failed to allocate Reset workitem\n")); NdisWriteErrorLogEntry(adapter->MiniportAdapterHandle, NDIS_ERROR_CODE_OUT_OF_RESOURCES, 0 ); ndisStatus = NDIS_STATUS_RESOURCES; break; } // Queue the workitem NdisQueueIoWorkItem(workitemHandle, MpResetWorkItem, adapter ); } while (FALSE); if (ndisStatus != NDIS_STATUS_PENDING) { // Something failed, clear the in reset flag MP_CLEAR_ADAPTER_STATUS(adapter, MP_ADAPTER_IN_RESET); } return ndisStatus; }
NDIS_STATUS HwResetHAL( __in PHW Hw, __in PHW_HAL_RESET_PARAMETERS ResetParams, __in BOOLEAN DispatchLevel ) { UNREFERENCED_PARAMETER(ResetParams); UNREFERENCED_PARAMETER(DispatchLevel); MPASSERT(!DispatchLevel); // Since we wait, we cannot be called at dispatch HW_ACQUIRE_HARDWARE_LOCK(Hw, FALSE); HW_SET_ADAPTER_STATUS(Hw, HW_ADAPTER_HAL_IN_RESET); HW_RELEASE_HARDWARE_LOCK(Hw, FALSE); // Wait for active send threads to finish. We dont wait // for anything else on an HAL reset since some of those // operations themselves may be causing the reset (Eg. channel // switch of a scan) HW_WAIT_FOR_ACTIVE_SENDS_TO_FINISH(Hw); HwDisableInterrupt(Hw, HW_ISR_TRACKING_HAL_RESET); if (ResetParams->FullReset) { // Perform a full reset of the HW HalResetStart(Hw->Hal); HalStop(Hw->Hal); // Reset the send and receive engine HwWaitForPendingReceives(Hw, NULL); HwResetSendEngine(Hw, FALSE); HwResetReceiveEngine(Hw, FALSE); // Remove old keys, etc HwClearNicState(Hw); // Reset our MAC & PHY state HwResetSoftwareMacState(Hw); HwResetSoftwarePhyState(Hw); HalStart(Hw->Hal, TRUE); // Push the new state on the hardware HwSetNicState(Hw); HalResetEnd(Hw->Hal); } else { // TODO: Currently we are overloading the HalSwitchChannel API for doing a HalReset HalSwitchChannel(Hw->Hal, Hw->PhyState.OperatingPhyId, HalGetPhyMIB(Hw->Hal, Hw->PhyState.OperatingPhyId)->Channel, FALSE ); HwResetReceiveEngine(Hw, FALSE); HwResetSendEngine(Hw, FALSE); HalStartReceive(Hw->Hal); } HwEnableInterrupt(Hw, HW_ISR_TRACKING_HAL_RESET); HW_CLEAR_ADAPTER_STATUS(Hw, HW_ADAPTER_HAL_IN_RESET); return NDIS_STATUS_SUCCESS; }
VOID HelperPortScanTimerCallback( _In_ PMP_HELPER_PORT HelperPort ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PMP_SCAN_PARAMETERS scanParameters; BOOLEAN queueExAccess = TRUE; MP_ACQUIRE_PORT_LOCK(HELPPORT_GET_MP_PORT(HelperPort), FALSE); MPASSERT(HelperPort->ScanContext.ActiveScanParameters != NULL); scanParameters = HelperPort->ScanContext.ActiveScanParameters; if (scanParameters->CancelScan) { // Scan is being cancelled, dont need to queue exclusive access MpTrace(COMP_SCAN, DBG_NORMAL, ("Ignored scan timer for cancelled/old scan request %p\n", scanParameters)); queueExAccess = FALSE; } else { scanParameters->State = SCAN_EXCLUSIVE_ACCESS_QUEUED; } MP_RELEASE_PORT_LOCK(HELPPORT_GET_MP_PORT(HelperPort), FALSE); if (queueExAccess) { // // Queue an exclusive access. The scan would be done in there // ndisStatus = HelperPortRequestExclusiveAccess(HelperPort, HelperPortScanExAccessCallback, HelperPort->ScanContext.ActiveScanParameters, FALSE ); if ((ndisStatus != NDIS_STATUS_SUCCESS) && (ndisStatus != NDIS_STATUS_PENDING)) { // Exclusive access request was rejected MpTrace(COMP_SCAN, DBG_SERIOUS, ("Exclusive access request in scan timer failed. Status = 0x%08x\n", ndisStatus)); HelperPortCompleteScanProcess( HelperPort, scanParameters, &ndisStatus ); } else if (NDIS_STATUS_SUCCESS == ndisStatus) { // the function completed synchronously, call the callback ourselves HelperPortScanExAccessCallback(HELPPORT_GET_MP_PORT(HelperPort), HelperPort->ScanContext.ActiveScanParameters); } } else { // Abort the scan ndisStatus = NDIS_STATUS_REQUEST_ABORTED; HelperPortCompleteScanProcess( HelperPort, scanParameters, &ndisStatus ); } }
VOID VNic11SendPackets( __in PVNIC pVNic, __in PMP_TX_MSDU PacketList, __in ULONG ulNumPkts, __in ULONG SendFlags ) { BOOLEAN fDispatchLevel = SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? TRUE : FALSE; BOOLEAN fFailSends = FALSE; #if DBG PMP_TX_MSDU currentPacket; ULONG myCount = 0; currentPacket = PacketList; while (currentPacket != NULL) { myCount++; currentPacket = MP_TX_MSDU_NEXT_MSDU(currentPacket); } MPASSERT(myCount == ulNumPkts); #endif VNicLockAtDispatch(pVNic, fDispatchLevel); do { if (VNicIsInReset(pVNic)) { MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC(%d) is in reset. Failing %d sends. \n", VNIC_PORT_NO, ulNumPkts)); fFailSends = TRUE; break; } if (VNicIsActive(pVNic) && Hw11CanTransmit(pVNic->pvHwContext) && (0 == PktQueueDepth(&pVNic->TxQueue))) { VNicSendPktsToHw(pVNic, ulNumPkts, PacketList, SendFlags); } else { /* We are either a. not currently active or b. the packets cannot be submitted to the hardware or c. there are packets pending in the send queue already Queue the send requests internally */ VNicQueueSendRequests(pVNic, ulNumPkts, PacketList); } } while (FALSE); VNicUnlockAtDispatch(pVNic, fDispatchLevel); if (fFailSends) { Port11SendCompletePackets(pVNic->pvPort, PacketList, SendFlags); } return; }
VOID BasePortIndicateReceivePackets( __in PMP_PORT Port, __in PMP_RX_MSDU PacketList, __in ULONG ReceiveFlags ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PMP_RX_MSDU currentPacket = PacketList, nextPacket; PMP_RX_MSDU packetListToIndicate = NULL, prevPacketToIndicate = NULL; PMP_RX_MSDU packetListToReturn = NULL, prevPacketToReturn = NULL; PNET_BUFFER_LIST nblChainToIndicate; ULONG indicateCount = 0; ULONG returnCount = 0; // // Currently the lower layer only indicates single MSDUs. We cannot handle // receiving more than one MSDU since we break the chain that the HW provides // and that is not acceptable in the RESOURCES case // MPASSERT(MP_RX_MSDU_NEXT_MSDU(currentPacket) == NULL); // Process each of the packets internally while (currentPacket != NULL) { nextPacket = MP_RX_MSDU_NEXT_MSDU(currentPacket); MP_RX_MSDU_NEXT_MSDU(currentPacket) = NULL; do { // // Pass the packet to the port to determine if this // packet should be indicated up to the OS or not // ndisStatus = Port11NotifyReceive(Port, currentPacket, ReceiveFlags); if (ndisStatus == NDIS_STATUS_SUCCESS) { // We only pass 1 packet at a time to the port, // lets look at whether we should indicate this packet up to // the OS or not ndisStatus = MP_RX_MSDU_STATUS(currentPacket); } if (ndisStatus != NDIS_STATUS_SUCCESS) { // This packet should not be indicated up break; } // Perform filtering of the packet to determine if this need to go up to the OS if (BasePortFilterFragment(Port, currentPacket) == FALSE) { // Drop these packets ndisStatus = NDIS_STATUS_NOT_ACCEPTED; // Since we have already given this packet to the port, let it undo // anything it may have done before Port11NotifyReturn(Port, currentPacket, NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags) ? NDIS_RETURN_FLAGS_DISPATCH_LEVEL : 0 ); break; } } while (FALSE); if (ndisStatus == NDIS_STATUS_SUCCESS) { // This packet gets indicated to the OS if (packetListToIndicate == NULL) { packetListToIndicate = currentPacket; } else { MP_RX_MSDU_NEXT_MSDU(prevPacketToIndicate) = currentPacket; } indicateCount++; prevPacketToIndicate = currentPacket; // // From this point on the packets must be completed // } else { // This (failed) packet gets returned back to the HW if (packetListToReturn == NULL) { packetListToReturn = currentPacket; } else { MP_RX_MSDU_NEXT_MSDU(prevPacketToReturn) = currentPacket; } returnCount++; prevPacketToReturn = currentPacket; } // Next packet currentPacket = nextPacket; } // Convert the packets we want to indicate into if (packetListToIndicate != NULL) { ndisStatus = BasePortTranslateRxPacketsToRxNBLs(Port, packetListToIndicate, &nblChainToIndicate ); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_SEND, DBG_SERIOUS, ("Failed to allocate NET_BUFFER_LIST chain for MP_RX_MSDU chain\n")); // // Notify port that these packets have been returned // Port11NotifyReturn(Port, packetListToIndicate, NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags) ? NDIS_RETURN_FLAGS_DISPATCH_LEVEL : 0 ); // // Prepend these "failed" MSDUs to the list that we wont indicate to the OS. These // would get freed below. The order doesnt matter here // currentPacket = packetListToIndicate; while (currentPacket != NULL) { nextPacket = MP_RX_MSDU_NEXT_MSDU(currentPacket); MP_RX_MSDU_NEXT_MSDU(currentPacket) = packetListToReturn; packetListToReturn = currentPacket; currentPacket = nextPacket; returnCount++; } } else { if (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags)) { // // Increment the counter for the number of packets we have submitted // to the OS. This would block the port from pausing, etc // PORT_ADD_PNP_REFCOUNT(Port, indicateCount); } // Indicate these to the OS NdisMIndicateReceiveNetBufferLists( Port->MiniportAdapterHandle, nblChainToIndicate, Port->PortNumber, indicateCount, ReceiveFlags ); if (NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags)) { // We wont get a return for these. Free the NBLs BasePortFreeTranslatedRxNBLs(Port, nblChainToIndicate); // Notify the ports about the return Port11NotifyReturn(Port, packetListToIndicate, NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags) ? NDIS_RETURN_FLAGS_DISPATCH_LEVEL : 0 ); } } } // Return all the non-used packets back to the HW (if we are permitted to call // return) if ((packetListToReturn != NULL) && (NDIS_TEST_RECEIVE_CAN_PEND(ReceiveFlags))) { VNic11ReturnPackets(Port->VNic, packetListToReturn, NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(ReceiveFlags) ? NDIS_RETURN_FLAGS_DISPATCH_LEVEL : 0 ); } }
NDIS_STATUS Hw11FindNic( __in PHW Hw, __out NDIS_ERROR_CODE* ErrorCode, __out PULONG ErrorValue ) { ULONG size; NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; UCHAR buffer[HW11_PCI_CONFIG_BUFFER_LENGTH]; PPCI_COMMON_CONFIG pciConfig = (PPCI_COMMON_CONFIG) buffer; // // Make sure adapter is present on the bus. // If present,verify the PCI configuration values. // do { // Load the PCI config information into our local buffer size = NdisMGetBusData(Hw->MiniportAdapterHandle, PCI_WHICHSPACE_CONFIG, FIELD_OFFSET(PCI_COMMON_CONFIG,VendorID), pciConfig, HW11_PCI_CONFIG_BUFFER_LENGTH ); if (size != HW11_PCI_CONFIG_BUFFER_LENGTH) { MpTrace(COMP_INIT_PNP, DBG_SERIOUS, ("NdisReadPciSlotInformation failed. Number of bytes of PCI config info returned is %d\n", size)); *ErrorCode = NDIS_ERROR_CODE_ADAPTER_NOT_FOUND; *ErrorValue = ERRLOG_READ_PCI_SLOT_FAILED; ndisStatus = NDIS_STATUS_ADAPTER_NOT_FOUND; break; } // // We have read the hardware ID, etc. Create the appropriate HAL // object that corresponds to our hardware. This allocates the HAL // structure and populates the HAL function pointers // #ifdef EXPORT_DRIVER_HAL // Invoke the export driver ndisStatus = HalDriverCreateWLANHal(Hw->MiniportAdapterHandle, pciConfig->VendorID, pciConfig->DeviceID, pciConfig->RevisionID, &Hw->Hal ); #else // Invoke the HAL library ndisStatus = HalCreateWLANHal(Hw->MiniportAdapterHandle, pciConfig->VendorID, pciConfig->DeviceID, pciConfig->RevisionID, &Hw->Hal ); #endif if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_INIT_PNP, DBG_SERIOUS, ("HalCreateWLANHal failed. Status = 0x%08x\n", ndisStatus)); *ErrorCode = NDIS_ERROR_CODE_ADAPTER_NOT_FOUND; *ErrorValue = ERRLOG_VENDOR_DEVICE_MISMATCH; break; } // // Let the HAL layer read the registry. This may depend on the NIC // and hence we do this here after we have found the NIC and create the HAL // and not during the first Hw11ReadRegistryConfiguration call // ndisStatus = HalReadRegistryConfiguration(Hw->Hal); if (ndisStatus != NDIS_STATUS_SUCCESS) { // Read registry should only fail for test purposes. If registry data is invalid the // driver should load the defaults and NOT fail driver initialize MpTrace(COMP_INIT_PNP, DBG_SERIOUS, ("HalReadRegistryConfiguration failed. Status = 0x%08x\n", ndisStatus)); MPASSERT(ndisStatus == NDIS_STATUS_RESOURCES); } // // Allow the HAL to look at PCI config information. This may also modify the // HAL // ndisStatus = HalParsePciConfiguration(Hw->Hal, buffer, size); if (ndisStatus != NDIS_STATUS_SUCCESS) { MpTrace(COMP_INIT_PNP, DBG_SERIOUS, ("HalParsePciConfiguration failed. Status = 0x%08x\n", ndisStatus)); *ErrorCode = NDIS_ERROR_CODE_UNSUPPORTED_CONFIGURATION; *ErrorValue = ERRLOG_INVALID_PCI_CONFIGURATION; break; } }while (FALSE); if (ndisStatus != NDIS_STATUS_SUCCESS) { // The only thing done so far is create the WLAN hal if (Hw->Hal != NULL) { HalFreeNic(Hw->Hal); Hw->Hal = NULL; } } return ndisStatus; }
// Programs the parameters from the PhyContext on the HW for the specified PhyID. // The PhyId is also made active. If CompletionCallback is NULL, the call completes // synchronously. If this is not NULL, the completion handler is always called even // in case of a failure NDIS_STATUS HwProgramPhy( _In_ PHW Hw, _In_ PHW_MAC_CONTEXT HwMac, _In_ ULONG PhyId, _In_ PHW_PHY_CONTEXT PhyContext, _In_opt_ HW_GENERIC_CALLBACK_FUNC CompletionCallback ) { NDIS_STATUS ndisStatus = NDIS_STATUS_PENDING; UCHAR i ,numActive = 0; BOOLEAN programmingDone = FALSE; do { // // if there are multiple active MACs program the channel only for the first one // for (i = 0; i < HW_MAX_NUMBER_OF_MAC; i++) { if (HW_MAC_CONTEXT_MUST_MERGE(&Hw->MacContext[i])) { numActive++; } } if (numActive > 1) { // These are multiple MACs active. MpTrace(COMP_MISC, DBG_NORMAL, ("Multiple MACs active. Not programming new phy setting\n")); // The new MAC should be programming the H/W on the current channel itself MPASSERT((Hw->PhyState.OperatingPhyId == PhyId) && (HalGetOperatingPhyMIB(Hw->Hal)->Channel == PhyContext->Channel)); // Success ndisStatus = NDIS_STATUS_SUCCESS; programmingDone = TRUE; break; } if (HwAwake(Hw, FALSE) != TRUE) { // Phy is OFF, we cannot finish this operation MpTrace(COMP_MISC, DBG_SERIOUS, ("Unable to activate PHY, not programming new phy settings\n")); ndisStatus = NDIS_STATUS_DOT11_POWER_STATE_INVALID; programmingDone = TRUE; break; } // We could optimize here by checking if we are already on the target Phy or Channel // and bailing out here // Only one phy programming call is permitted MPASSERT(Hw->PhyState.PendingPhyProgramCallback == NULL); if (CompletionCallback != NULL) { // Save the MAC context, etc Hw->PhyState.PhyProgramMacContext = HwMac; Hw->PhyState.PendingPhyProgramCallback = CompletionCallback; Hw->PhyState.DestinationPhyId = PhyId; Hw->PhyState.NewPhyContext = PhyContext; // Add a ref since we are going to do an async operation HW_INCREMENT_ACTIVE_OPERATION_REF(Hw); // Caller is OK with asynchronous behavior NdisQueueIoWorkItem( Hw->PhyState.PhyProgramWorkItem, HwPhyProgramWorkItem, Hw ); // Completed asynchronously ndisStatus = NDIS_STATUS_PENDING; } else { // Caller expect synchronous behavior. Do the work inline ndisStatus = HwSetPhyContext(Hw, PhyId, PhyContext ); } }while (FALSE); if (programmingDone) { if (CompletionCallback != NULL) { // Call the completion handler CompletionCallback(Hw, HwMac, &ndisStatus); // Already called the complete ndisStatus = NDIS_STATUS_PENDING; } else { // Caller expects synchronous behavior // we are done } } return ndisStatus; }
// Write lock on the list must be held // Returns an entry that can be expired. The caller frees it PMP_BSS_ENTRY HelperPortExpireBSSEntry( __in PMP_BSS_LIST pBSSList, __in ULONGLONG ullMaxActiveTime, __in ULONGLONG ullExpireTimeStamp ) { PLIST_ENTRY pHead = NULL, pEntry = NULL; PMP_BSS_ENTRY pBSSEntry = NULL; BOOLEAN bFound = FALSE; // // We can expire an entry that has been around for longer // than this time // if (ullMaxActiveTime <= ullExpireTimeStamp) ullExpireTimeStamp -= ullMaxActiveTime; pHead = &(pBSSList->List); pEntry = pHead->Flink; while(pEntry != pHead) { pBSSEntry = CONTAINING_RECORD(pEntry, MP_BSS_ENTRY, Link); pEntry = pEntry->Flink; NdisAcquireSpinLock(&(pBSSEntry->Lock)); // // If the entry is older than we expected and its not in // use, we can expire it // if (pBSSEntry->HostTimestamp < ullExpireTimeStamp) { if (NdisInterlockedDecrement(&(pBSSEntry->RefCount)) == 0) { MpTrace(COMP_SCAN, DBG_LOUD, ("Expiring AP: %02X-%02X-%02X-%02X-%02X-%02X\n", pBSSEntry->Dot11BSSID[0], pBSSEntry->Dot11BSSID[1], pBSSEntry->Dot11BSSID[2], pBSSEntry->Dot11BSSID[3], pBSSEntry->Dot11BSSID[4], pBSSEntry->Dot11BSSID[5])); MPASSERT(pBSSEntry->pAssocRequest == NULL); MPASSERT(pBSSEntry->pAssocResponse == NULL); // // This is the entry we can expire. Remove it from the list. // NdisReleaseSpinLock(&(pBSSEntry->Lock)); HelperPortRemoveBSSEntry(pBSSList, pBSSEntry); bFound = TRUE; break; } else { // Someone is using the entry, we cannot remove/delete this. Add back // a ref and we will delete later on // This is subobtimal. Ideally the last person to decrement // refcount should delete the entry and not us. Modify to remove // the entry from the list, decrement its refcount and only free // the memory if the refcount has gone to zero NdisInterlockedIncrement(&(pBSSEntry->RefCount)); } } NdisReleaseSpinLock(&(pBSSEntry->Lock)); } if (bFound != TRUE) { pBSSEntry = NULL; } return pBSSEntry;
// Scans a set of channels. Called with exclusive access held. Also only called when we have a channel // to scan. VOID HelperPortStartPartialScan( _In_ PMP_HELPER_PORT HelperPort, _In_ PMP_SCAN_PARAMETERS ScanParameters ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; ULONG i, currentChannelCount; PMP_SCAN_CHANNEL_LIST currentChannelList; do { MpTrace(COMP_SCAN, DBG_LOUD, ("Starting partial scan of %p\n", ScanParameters)); MP_ACQUIRE_PORT_LOCK(HELPPORT_GET_MP_PORT(HelperPort), FALSE); // Check if the request should be cancelled if (ScanParameters->CancelScan) { MpTrace(COMP_SCAN, DBG_NORMAL, ("Aborting partial scan for cancelled/old scan request %p\n", ScanParameters)); ndisStatus = NDIS_STATUS_REQUEST_ABORTED; } else { // We are going to send this on the hardware ScanParameters->State = SCAN_HARDWARE_SCANNING; } MP_RELEASE_PORT_LOCK(HELPPORT_GET_MP_PORT(HelperPort), FALSE); if (ndisStatus != NDIS_STATUS_SUCCESS) { break; } // Determine the channels to scan. If specified by the port, use those, else // use our if (ScanParameters->PortScanRequest->ChannelCount != 0) { currentChannelCount = ScanParameters->PortScanRequest->ChannelCount; // // Populate the channels for this scan with what the port requested // ScanParameters->VNicScanRequest.ChannelCount = currentChannelCount; for (i = 0; i < currentChannelCount; i++) { ScanParameters->VNicScanRequest.ChannelList[i] = ScanParameters->PortScanRequest->ChannelList[i]; } ScanParameters->VNicScanRequest.PhyId = ScanParameters->PortScanRequest->PhyId; } else { currentChannelList = &HelperPort->ScanContext.ScanChannels[ScanParameters->CurrentPhyIndex]; // We must have atleast one channel in the current phy index that we can scan MPASSERT(currentChannelList->ChannelCount > ScanParameters->NextChannelIndex); // Determine the number of channels to use for this scan currentChannelCount = ScanParameters->MaxChannelCount; if ((ScanParameters->NextChannelIndex + currentChannelCount) > currentChannelList->ChannelCount) { // We have fewer remaining channels that our MaxChannelCount, adjust the channel count currentChannelCount = currentChannelList->ChannelCount - ScanParameters->NextChannelIndex; } // // Populate the channels for this scan // ScanParameters->VNicScanRequest.ChannelCount = currentChannelCount; for (i = 0; i < currentChannelCount; i++) { ScanParameters->VNicScanRequest.ChannelList[i] = currentChannelList->ChannelList[ScanParameters->NextChannelIndex + i]; } ScanParameters->VNicScanRequest.PhyId = currentChannelList->PhyId; // Next time we start scan at the next channel ScanParameters->NextChannelIndex = ScanParameters->NextChannelIndex + currentChannelCount; } ndisStatus = VNic11StartScan(HELPPORT_GET_VNIC(HelperPort), &ScanParameters->VNicScanRequest, HelperPortScanCompleteCallback ); if (ndisStatus == NDIS_STATUS_SUCCESS) { // the function completed synchronously - call the callback ourselves HelperPortScanCompleteCallback(HELPPORT_GET_MP_PORT(HelperPort), &ndisStatus); } else if (NDIS_STATUS_PENDING != ndisStatus) { MpTrace(COMP_SCAN, DBG_SERIOUS, ("VNic11StartScan failed for channel index %d\n", ScanParameters->NextChannelIndex - currentChannelCount)); break; } } while (FALSE); if (ndisStatus != NDIS_STATUS_PENDING && ndisStatus != NDIS_STATUS_SUCCESS) { // We call the complete function HelperPortCompletePartialScan(HelperPort, ScanParameters, &ndisStatus); } }
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); }
/** * This routine is called to free memory which was previously allocated using MpAllocateMemory function. * Before freeing the memory, this function checks and makes sure that no overflow or underflows have * happened and will also try to detect multiple frees of the same memory chunk. * * \warning Do not use this function directly. Using MP_FREE_MEMORY ensures that * this function gets called for debug version of the driver. Retail builds will use Ndis API * for freeing of memory * \param Memory Pointer to memory allocated using MP_FREE_MEMORY * \sa MpAllocateMemory, MP_ALLOCATE_MEMORY, MP_FREE_MEMORY, NdisFreeMemory, MpFreeAllocatedBlocks */ VOID MpFreeMemory ( PVOID Memory ) { PMP_MEMORY_BLOCK memoryBlockHeader; MPASSERTMSG ("NULL memory being freed", Memory); MPASSERTMSG ("Allocated blocks list is empty. This is an extra free\n", !IsListEmpty(&GlobalMemoryList)); // Jump back by memory header size so we can get to the header memoryBlockHeader = (PMP_MEMORY_BLOCK) (((PUCHAR)Memory) - sizeof(MP_MEMORY_BLOCK)); // // Check that header was not corrupted // if (memoryBlockHeader->HeaderPattern != MP_HEADER_PATTERN) { if (memoryBlockHeader->HeaderPattern == MP_FREED_PATTERN) { MpTrace(COMP_DBG, DBG_SERIOUS, ("Possible double free of memory block at %p\n", memoryBlockHeader)); } else { MpTrace(COMP_DBG, DBG_SERIOUS, ("Memory corruption due to underflow detected at memory block %p\n", memoryBlockHeader)); } MpTrace(COMP_DBG, DBG_SERIOUS, ("Dumping information about memory block. This information may itself have been corrupted and could cause machine to bugcheck.\n")); MpTrace(COMP_DBG, DBG_SERIOUS, ("Memory was allocated from %s at line %d\n", memoryBlockHeader->File, memoryBlockHeader->Length)); MPASSERT (FALSE); } // // Now corrupt the header so that double frees will fail. // Note simultaneous frees of same memory will not get caught this way! // memoryBlockHeader->HeaderPattern = MP_FREED_PATTERN; // // Check that trailer was not corrupted // if (*(PULONG) ((PUCHAR)Memory + memoryBlockHeader->Length) != MP_TRAILER_PATTERN) { MpTrace(COMP_DBG, DBG_SERIOUS, ("Memory corruption due to overflow detected at %p\n", Memory)); MpTrace(COMP_DBG, DBG_SERIOUS, ("Dumping information about memory block. This information may itself have been corrupted and could cause machine to bugcheck.\n")); MpTrace(COMP_DBG, DBG_SERIOUS, ("Memory was allocated from %s at line %d\n", memoryBlockHeader->File, memoryBlockHeader->Length)); MPASSERT (FALSE); } // // Remove this memory block from the list of allocations // NdisAcquireSpinLock (&GlobalMemoryLock); RemoveEntryList (&memoryBlockHeader->ListEntry); NdisReleaseSpinLock (&GlobalMemoryLock); // // Zero out data and trailer and then free the memory chunk back to the OS // NdisZeroMemory (Memory, sizeof (ULONG) + (memoryBlockHeader->Length)); NdisFreeMemory (memoryBlockHeader, 0, 0); }
NDIS_STATUS HelperPortFlushBSSList( __in PMP_PORT Port ) { MP_RW_LOCK_STATE LockState; PLIST_ENTRY pListEntry; PMP_BSS_ENTRY pBSSEntry = NULL; LONG APRefCount; LIST_ENTRY TempList; PMP_HELPER_PORT HelperPort = MP_GET_HELPPORT(Port); PMP_BSS_LIST pDiscoveredBSSList = &(HelperPort->BSSList); // // Entries that are currently in use (eg for connection) // we cannot flush and instead would put in the temporary queue // InitializeListHead(&TempList); MP_ACQUIRE_WRITE_LOCK(&(HelperPort->BSSList.ListLock), &LockState); while (!IsListEmpty(&(pDiscoveredBSSList->List))) { pListEntry = RemoveHeadList(&(pDiscoveredBSSList->List)); pBSSEntry = CONTAINING_RECORD(pListEntry, MP_BSS_ENTRY, Link); APRefCount = NdisInterlockedDecrement(&(pBSSEntry->RefCount)); if (APRefCount == 0) { NdisAcquireSpinLock(&(pBSSEntry->Lock)); MPASSERT(pBSSEntry->pAssocRequest == NULL); MPASSERT(pBSSEntry->pAssocResponse == NULL); if (pBSSEntry->pDot11BeaconFrame != NULL) { MP_FREE_MEMORY(pBSSEntry->pDot11BeaconFrame); pBSSEntry->pDot11BeaconFrame = NULL; pBSSEntry->BeaconFrameSize = 0; pBSSEntry->MaxBeaconFrameSize= 0; } if (pBSSEntry->pDot11ProbeFrame != NULL) { MP_FREE_MEMORY(pBSSEntry->pDot11ProbeFrame); pBSSEntry->pDot11ProbeFrame = NULL; pBSSEntry->ProbeFrameSize = 0; pBSSEntry->MaxProbeFrameSize= 0; } pBSSEntry->pDot11InfoElemBlob = NULL; pBSSEntry->InfoElemBlobSize = 0; NdisReleaseSpinLock(&(pBSSEntry->Lock)); MP_FREE_MEMORY(pBSSEntry); } else { // Restore refcount and save for adding back to list NdisInterlockedIncrement(&(pBSSEntry->RefCount)); InsertTailList(&TempList, pListEntry); } } pDiscoveredBSSList->NumOfBSSEntries = 0; // // Restore entries that are in use // while (!IsListEmpty(&TempList)) { pListEntry = RemoveHeadList(&TempList); InsertTailList(&(pDiscoveredBSSList->List), pListEntry); pDiscoveredBSSList->NumOfBSSEntries++; } // Since our scan list is flushed, also clear the last scan time HelperPort->ScanContext.LastScanTime = 0; MP_RELEASE_WRITE_LOCK(&(HelperPort->BSSList.ListLock), &LockState); return NDIS_STATUS_SUCCESS;