blob: a4b14d6e0c1e55da33f7f09856f06065377b6996 [file] [log] [blame]
/*
* Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/*
* This file limLinkMonitoringAlgo.cc contains the code for
* Link monitoring algorithm on AP and heart beat failure
* handling on STA.
* Author: Chandra Modumudi
* Date: 03/01/02
* History:-
* Date Modified by Modification Information
* --------------------------------------------------------------------
*
*/
#include "aniGlobal.h"
#include "wni_cfg.h"
#include "cfgApi.h"
#include "schApi.h"
#include "pmmApi.h"
#include "utilsApi.h"
#include "limAssocUtils.h"
#include "limTypes.h"
#include "limUtils.h"
#include "limPropExtsUtils.h"
#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM //FEATURE_WLAN_DIAG_SUPPORT
#include "vos_diag_core_log.h"
#endif //FEATURE_WLAN_DIAG_SUPPORT
#ifdef WLAN_FEATURE_VOWIFI_11R
#include "limFTDefs.h"
#endif
#include "limSession.h"
#include "limSerDesUtils.h"
/**
* limSendKeepAliveToPeer()
*
*FUNCTION:
* This function is called to send Keep alive message to peer
*
*LOGIC:
*
*ASSUMPTIONS:
*
*NOTE:
* NA
*
* @param pMac - Pointer to Global MAC structure
* @return None
*/
void
limSendKeepAliveToPeer(tpAniSirGlobal pMac)
{
} /*** limSendKeepAliveToPeer() ***/
/** ---------------------------------------------------------
\fn limDeleteStaContext
\brief This function handles the message from HAL:
\ WDA_DELETE_STA_CONTEXT_IND. This function
\ validates that the given station id exist, and if so,
\ deletes the station by calling limTriggerSTAdeletion.
\param tpAniSirGlobal pMac
\param tpSirMsgQ limMsg
\return none
-----------------------------------------------------------*/
void
limDeleteStaContext(tpAniSirGlobal pMac, tpSirMsgQ limMsg)
{
tpDeleteStaContext pMsg = (tpDeleteStaContext)limMsg->bodyptr;
tpDphHashNode pStaDs;
tpPESession psessionEntry ;
if(NULL == pMsg)
{
PELOGE(limLog(pMac, LOGE,FL("Invalid body pointer in message"));)
return;
}
psessionEntry = pe_find_session_by_sme_session_id(pMac, pMsg->vdev_id);
if(NULL == psessionEntry)
{
limLog(pMac, LOGE, FL("session not found for given sme session"));
vos_mem_free(pMsg);
return;
}
switch(pMsg->reasonCode)
{
case HAL_DEL_STA_REASON_CODE_KEEP_ALIVE:
pStaDs = dphLookupAssocId(pMac, pMsg->staId, &pMsg->assocId,
&psessionEntry->dph.dphHashTable);
if (!pStaDs) {
PELOGE(limLog(pMac, LOGE,
FL("Skip STA deletion (invalid STA) limSystemRole=%d"),
GET_LIM_SYSTEM_ROLE(psessionEntry));)
vos_mem_free(pMsg);
return;
}
/*
* check and see if same staId. This is to avoid the scenario
* where we're trying to delete a staId we just added.
*/
if (pStaDs->staIndex != pMsg->staId) {
PELOGE(limLog(pMac, LOGE,
FL("staid mismatch: %d vs %d "), pStaDs->staIndex, pMsg->staId);)
vos_mem_free(pMsg);
return;
}
/*
* Check if Deauth/Disassoc is triggered from Host.
* If mlmState is in some transient state then
* don't trigger STA deletion to avoid the race
* condition.
*/
if ((pStaDs &&
((pStaDs->mlmStaContext.mlmState !=
eLIM_MLM_LINK_ESTABLISHED_STATE) &&
(pStaDs->mlmStaContext.mlmState !=
eLIM_MLM_WT_ASSOC_CNF_STATE) &&
(pStaDs->mlmStaContext.mlmState !=
eLIM_MLM_ASSOCIATED_STATE)))) {
PELOGE(limLog(pMac, LOGE,
FL("received Del STA context in some transit state(staId: %d, assocId: %d)"),
pMsg->staId, pMsg->assocId);)
vos_mem_free(pMsg);
return;
}
if (LIM_IS_STA_ROLE(psessionEntry) && !pMsg->is_tdls) {
/*
* If roaming is in progress, then ignore the STA kick out
* and let the connection happen. The roaming_in_progress
* flag is set whenever a candidate found indication is
* received. It is enabled on the PE session for which
* the indication is received. There is really no need to
* re-set the flag, since the PE session on which it was
* set will be deleted, even if roaming is success or failure.
* When roaming is a success, the PE session for AP1 is
* deleted. When we get a candidate indication, it would be
* on the PE session of the AP1. AP2 to which we are about to
* roam will have a new PE session ID.If roaming fails for
* any reason, then it will anyways delete the PE session of
* of the AP1.
*/
if (psessionEntry->roaming_in_progress ||
limIsReassocInProgress(pMac, psessionEntry)) {
limLog(pMac, LOGE,
FL("roam_progress=%d, reassoc=%d. Not disconnecting"),
psessionEntry->roaming_in_progress,
limIsReassocInProgress(pMac, psessionEntry));
vos_mem_free(pMsg);
return;
}
pStaDs = dphGetHashEntry(pMac,
DPH_STA_HASH_INDEX_PEER,
&psessionEntry->dph.dphHashTable);
if (NULL == pStaDs) {
limLog(pMac, LOGE, FL("Dph entry not found."));
vos_mem_free(pMsg);
return;
}
pStaDs->del_sta_ctx_rssi = pMsg->rssi;
limSendDeauthMgmtFrame(pMac,
eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON,
pMsg->addr2, psessionEntry, FALSE);
limTearDownLinkWithAp(pMac, psessionEntry->peSessionId,
eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON);
/* only break for STA role (non TDLS) */
break;
}
limLog(pMac, LOGE, FL("Deleting sta: staId %d, reasonCode %d"),
pMsg->staId, pMsg->reasonCode);
if (LIM_IS_IBSS_ROLE(psessionEntry)) {
vos_mem_free(pMsg);
return;
}
if (LIM_IS_BT_AMP_AP_ROLE(psessionEntry) ||
LIM_IS_AP_ROLE(psessionEntry)) {
PELOG1(limLog(pMac, LOG1, FL("SAP:lim Delete Station Context (staId: %d, assocId: %d) "),
pMsg->staId, pMsg->assocId);)
limSendDisassocMgmtFrame(pMac,
eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON,
pStaDs->staAddr, psessionEntry, FALSE);
limTriggerSTAdeletion(pMac, pStaDs, psessionEntry);
}
#ifdef FEATURE_WLAN_TDLS
else
{
if (LIM_IS_STA_ROLE(psessionEntry) &&
STA_ENTRY_TDLS_PEER == pStaDs->staType) {
//TeardownLink with PEER
//Reason code HAL_DEL_STA_REASON_CODE_KEEP_ALIVE means
//eSIR_MAC_TDLS_TEARDOWN_PEER_UNREACHABLE
limSendSmeTDLSDelStaInd(pMac, pStaDs, psessionEntry,
eSIR_MAC_TDLS_TEARDOWN_PEER_UNREACHABLE);
}
}
#endif
break;
case HAL_DEL_STA_REASON_CODE_UNKNOWN_A2:
PELOGE(limLog(pMac, LOGE, FL(" Deleting Unknown station "));)
limPrintMacAddr(pMac, pMsg->addr2, LOGE);
limSendDeauthMgmtFrame( pMac, eSIR_MAC_CLASS3_FRAME_FROM_NON_ASSOC_STA_REASON, pMsg->addr2, psessionEntry, FALSE);
break;
default:
PELOGE(limLog(pMac, LOGE, FL(" Unknown reason code "));)
break;
}
vos_mem_free(pMsg);
return;
}
/**
* limTriggerSTAdeletion()
*
*FUNCTION:
* This function is called to trigger STA context deletion
*
*LOGIC:
*
*ASSUMPTIONS:
*
*NOTE:
* NA
*
* @param pMac - Pointer to global MAC structure
* @param pStaDs - Pointer to internal STA Datastructure
* @return None
*/
void
limTriggerSTAdeletion(tpAniSirGlobal pMac, tpDphHashNode pStaDs, tpPESession psessionEntry)
{
tLimMlmDisassocInd mlmDisassocInd;
if (!pStaDs)
{
PELOGW(limLog(pMac, LOGW, FL("Skip STA deletion (invalid STA)"));)
return;
}
if ((pStaDs->mlmStaContext.mlmState == eLIM_MLM_WT_DEL_STA_RSP_STATE) ||
(pStaDs->mlmStaContext.mlmState == eLIM_MLM_WT_DEL_BSS_RSP_STATE) ||
pStaDs->sta_deletion_in_progress) {
/* Already in the process of deleting context for the peer */
limLog(pMac, LOG1,
FL("Deletion is in progress (%d) for peer:%pK in mlmState %d"),
pStaDs->sta_deletion_in_progress, pStaDs->staAddr,
pStaDs->mlmStaContext.mlmState);
return;
}
pStaDs->sta_deletion_in_progress = true;
pStaDs->mlmStaContext.disassocReason =
eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON;
pStaDs->mlmStaContext.cleanupTrigger = eLIM_LINK_MONITORING_DISASSOC;
vos_mem_copy(&mlmDisassocInd.peerMacAddr, pStaDs->staAddr,
sizeof(tSirMacAddr));
mlmDisassocInd.reasonCode = eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON;
mlmDisassocInd.disassocTrigger = eLIM_LINK_MONITORING_DISASSOC;
/* Update PE session Id */
mlmDisassocInd.sessionId = psessionEntry->peSessionId;
limPostSmeMessage(pMac, LIM_MLM_DISASSOC_IND,
(tANI_U32 *) &mlmDisassocInd);
// Issue Disassoc Indication to SME.
limSendSmeDisassocInd(pMac, pStaDs, psessionEntry);
} /*** end limTriggerSTAdeletion() ***/
/**
* limTearDownLinkWithAp()
*
*FUNCTION:
* This function is called when heartbeat (beacon reception)
* fails on STA
*
*LOGIC:
*
*ASSUMPTIONS:
*
*NOTE:
*
* @param pMac - Pointer to Global MAC structure
* @return None
*/
void
limTearDownLinkWithAp(tpAniSirGlobal pMac, tANI_U8 sessionId, tSirMacReasonCodes reasonCode)
{
tpDphHashNode pStaDs = NULL;
//tear down the following sessionEntry
tpPESession psessionEntry;
if((psessionEntry = peFindSessionBySessionId(pMac, sessionId))== NULL)
{
limLog(pMac, LOGP,FL("Session Does not exist for given sessionID"));
return;
}
/**
* Heart beat failed for upto threshold value
* and AP did not respond for Probe request.
* Trigger link tear down.
*/
if(pMac->psOffloadEnabled)
psessionEntry->pmmOffloadInfo.bcnmiss = FALSE;
else
pMac->pmm.inMissedBeaconScenario = FALSE;
limLog(pMac, LOGW,
FL("No ProbeRsp from AP after HB failure. Tearing down link"));
// Deactivate heartbeat timer
limHeartBeatDeactivateAndChangeTimer(pMac, psessionEntry);
// Announce loss of link to Roaming algorithm
// and cleanup by sending SME_DISASSOC_REQ to SME
pStaDs = dphGetHashEntry(pMac, DPH_STA_HASH_INDEX_PEER, &psessionEntry->dph.dphHashTable);
if (pStaDs != NULL)
{
tLimMlmDeauthInd mlmDeauthInd;
#ifdef FEATURE_WLAN_TDLS
/* Delete all TDLS peers connected before leaving BSS*/
limDeleteTDLSPeers(pMac, psessionEntry);
#endif
pStaDs->mlmStaContext.disassocReason = reasonCode;
pStaDs->mlmStaContext.cleanupTrigger = eLIM_LINK_MONITORING_DEAUTH;
/// Issue Deauth Indication to SME.
vos_mem_copy((tANI_U8 *) &mlmDeauthInd.peerMacAddr,
pStaDs->staAddr,
sizeof(tSirMacAddr));
/*
* if sendDeauthBeforeCon is enabled and reasoncode is
* Beacon Missed Store the MAC of AP in the flip flop
* buffer. This MAC will be used to send Deauth before
* connection, if we connect to same AP after HB failure.
*/
if (pMac->roam.configParam.sendDeauthBeforeCon &&
eSIR_BEACON_MISSED == reasonCode)
{
int apCount = pMac->lim.gLimHeartBeatApMacIndex;
if (pMac->lim.gLimHeartBeatApMacIndex)
pMac->lim.gLimHeartBeatApMacIndex = 0;
else
pMac->lim.gLimHeartBeatApMacIndex = 1;
limLog(pMac, LOGE, FL("HB Failure on MAC "
MAC_ADDRESS_STR" Store it on Index %d"),
MAC_ADDR_ARRAY(pStaDs->staAddr),apCount);
sirCopyMacAddr(pMac->lim.gLimHeartBeatApMac[apCount],
pStaDs->staAddr);
}
mlmDeauthInd.reasonCode = (tANI_U8) pStaDs->mlmStaContext.disassocReason;
mlmDeauthInd.deauthTrigger = pStaDs->mlmStaContext.cleanupTrigger;
if (GET_LIM_SYSTEM_ROLE(psessionEntry) == eLIM_STA_ROLE)
limPostSmeMessage(pMac, LIM_MLM_DEAUTH_IND,
(tANI_U32 *) &mlmDeauthInd);
limSendSmeDeauthInd(pMac, pStaDs, psessionEntry);
limReInitScanResults(pMac);
}
} /*** limTearDownLinkWithAp() ***/
/**
* limHandleHeartBeatFailure()
*
*FUNCTION:
* This function is called when heartbeat (beacon reception)
* fails on STA
*
*LOGIC:
*
*ASSUMPTIONS:
*
*NOTE:
*
* @param pMac - Pointer to Global MAC structure
* @return None
*/
void limHandleHeartBeatFailure(tpAniSirGlobal pMac,tpPESession psessionEntry)
{
#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM //FEATURE_WLAN_DIAG_SUPPORT
vos_log_beacon_update_pkt_type *log_ptr = NULL;
#endif //FEATURE_WLAN_DIAG_SUPPORT
/* If gLimHeartBeatTimer fires between the interval of sending WDA_ENTER_BMPS_REQUEST
* to the HAL and receiving WDA_ENTER_BMPS_RSP from the HAL, then LIM (PE) tries to Process the
* SIR_LIM_HEAR_BEAT_TIMEOUT message but The PE state is ePMM_STATE_BMPS_SLEEP so PE dont
* want to handle heartbeat timeout in the BMPS, because Firmware handles it in BMPS.
* So just return from heartbeatfailure handler
*/
if(!pMac->psOffloadEnabled)
{
if(!IS_ACTIVEMODE_OFFLOAD_FEATURE_ENABLE && (!limIsSystemInActiveState(pMac)))
return;
}
#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM //FEATURE_WLAN_DIAG_SUPPORT
WLAN_VOS_DIAG_LOG_ALLOC(log_ptr, vos_log_beacon_update_pkt_type, LOG_WLAN_BEACON_UPDATE_C);
if(log_ptr)
log_ptr->bcn_rx_cnt = psessionEntry->LimRxedBeaconCntDuringHB;
WLAN_VOS_DIAG_LOG_REPORT(log_ptr);
#endif //FEATURE_WLAN_DIAG_SUPPORT
/* Ensure HB Status for the session has been reseted */
psessionEntry->LimHBFailureStatus = eANI_BOOLEAN_FALSE;
if ((LIM_IS_STA_ROLE(psessionEntry) ||
LIM_IS_BT_AMP_STA_ROLE(psessionEntry)) &&
(psessionEntry->limMlmState == eLIM_MLM_LINK_ESTABLISHED_STATE) &&
(psessionEntry->limSmeState != eLIM_SME_WT_DISASSOC_STATE) &&
(psessionEntry->limSmeState != eLIM_SME_WT_DEAUTH_STATE))
{
if (!pMac->sys.gSysEnableLinkMonitorMode)
return;
/* Ignore HB if channel switch is in progress */
if (psessionEntry->gLimSpecMgmt.dot11hChanSwState ==
eLIM_11H_CHANSW_RUNNING) {
limLog(pMac, LOGE,
FL("Ignore Heartbeat failure as Channel switch is in progress"));
pMac->pmm.inMissedBeaconScenario = false;
return;
}
/**
* Beacon frame not received within heartbeat timeout.
*/
limLog(pMac, LOGW, FL("Heartbeat Failure"));
pMac->lim.gLimHBfailureCntInLinkEstState++;
/**
* Check if connected on the DFS channel, if not connected on
* DFS channel then only send the probe request otherwise tear down the link
*/
if(!limIsconnectedOnDFSChannel(psessionEntry->currentOperChannel))
{
/*** Detected continuous Beacon Misses ***/
psessionEntry->LimHBFailureStatus= eANI_BOOLEAN_TRUE;
/*Reset the HB packet count before sending probe*/
limResetHBPktCount(psessionEntry);
/**
* Send Probe Request frame to AP to see if
* it is still around. Wait until certain
* timeout for Probe Response from AP.
*/
PELOGW(limLog(pMac, LOGW, FL("Heart Beat missed from AP. Sending Probe Req"));)
/* for searching AP, we don't include any additional IE */
limSendProbeReqMgmtFrame(pMac, &psessionEntry->ssId, psessionEntry->bssId,
psessionEntry->currentOperChannel,psessionEntry->selfMacAddr,
psessionEntry->dot11mode, 0, NULL);
}
else
{
PELOGW(limLog(pMac, LOGW,
FL("Heart Beat missed from AP on DFS chanel moving to passive"));)
if (psessionEntry->currentOperChannel < SIR_MAX_24G_5G_CHANNEL_RANGE){
limCovertChannelScanType(pMac, psessionEntry->currentOperChannel, false);
pMac->lim.dfschannelList.timeStamp[psessionEntry->currentOperChannel] = 0;
}
/* Connected on DFS channel so should not send the probe request
* tear down the link directly */
limTearDownLinkWithAp(pMac, psessionEntry->peSessionId,
eSIR_BEACON_MISSED);
}
}
else
{
/**
* Heartbeat timer may have timed out
* while we're doing background scanning/learning
* or in states other than link-established state.
* Log error.
*/
PELOG1(limLog(pMac, LOG1, FL("received heartbeat timeout in state %X"),
psessionEntry->limMlmState);)
limPrintMlmState(pMac, LOG1, psessionEntry->limMlmState);
pMac->lim.gLimHBfailureCntInOtherStates++;
limReactivateHeartBeatTimer(pMac, psessionEntry);
}
} /*** limHandleHeartBeatFailure() ***/