blob: 2ce55b062a117deeb25162100038929fe20399d5 [file] [log] [blame]
/*
* Copyright (c) 2012-2015 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 lim_link_monitoring_algo.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 "ani_global.h"
#include "wni_cfg.h"
#include "cfg_api.h"
#include "sch_api.h"
#include "utils_api.h"
#include "lim_assoc_utils.h"
#include "lim_types.h"
#include "lim_utils.h"
#include "lim_prop_exts_utils.h"
#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */
#include "host_diag_core_log.h"
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
#ifdef WLAN_FEATURE_VOWIFI_11R
#include "lim_ft_defs.h"
#endif
#include "lim_session.h"
#include "lim_ser_des_utils.h"
/**
* lim_delete_sta_util - utility function for deleting station context
*
* @mac_ctx: global MAC context
* @msg: pointer to delte station context
* @session_entry: PE session entry
*
* utility function called to clear up station context.
*
* Return: None.
*/
static void lim_delete_sta_util(tpAniSirGlobal mac_ctx, tpDeleteStaContext msg,
tpPESession session_entry)
{
tpDphHashNode stads;
lim_log(mac_ctx, LOGE,
FL("Deleting station: staId = %d, reasonCode = %d"),
msg->staId, msg->reasonCode);
if (LIM_IS_IBSS_ROLE(session_entry)) {
cdf_mem_free(msg);
return;
}
stads = dph_lookup_assoc_id(mac_ctx, msg->staId, &msg->assocId,
&session_entry->dph.dphHashTable);
if (!stads) {
lim_log(mac_ctx, LOGE,
FL("Invalid STA limSystemRole=%d"),
GET_LIM_SYSTEM_ROLE(session_entry));
cdf_mem_free(msg);
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 (stads->staIndex != msg->staId) {
lim_log(mac_ctx, LOGE, FL("staid mismatch: %d vs %d "),
stads->staIndex, msg->staId);
cdf_mem_free(msg);
return;
}
if (LIM_IS_BT_AMP_AP_ROLE(session_entry) ||
LIM_IS_AP_ROLE(session_entry)) {
lim_log(mac_ctx, LOG1,
FL("Delete Station staId: %d, assocId: %d"),
msg->staId, msg->assocId);
/*
* 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 ((stads &&
((stads->mlmStaContext.mlmState !=
eLIM_MLM_LINK_ESTABLISHED_STATE) &&
(stads->mlmStaContext.mlmState !=
eLIM_MLM_WT_ASSOC_CNF_STATE) &&
(stads->mlmStaContext.mlmState !=
eLIM_MLM_ASSOCIATED_STATE)))) {
lim_log(mac_ctx, LOGE,
FL("Inv Del STA staId:%d, assocId:%d"),
msg->staId, msg->assocId);
cdf_mem_free(msg);
return;
} else {
lim_trigger_sta_deletion(mac_ctx, stads, session_entry);
}
} else {
#ifdef FEATURE_WLAN_TDLS
if (LIM_IS_STA_ROLE(session_entry) &&
STA_ENTRY_TDLS_PEER == stads->staType) {
/*
* TeardownLink with PEER reason code
* HAL_DEL_STA_REASON_CODE_KEEP_ALIVE means
* eSIR_MAC_TDLS_TEARDOWN_PEER_UNREACHABLE
*/
lim_send_sme_tdls_del_sta_ind(mac_ctx, stads,
session_entry,
eSIR_MAC_TDLS_TEARDOWN_PEER_UNREACHABLE);
} else {
#endif
/* TearDownLink with AP */
tLimMlmDeauthInd mlm_deauth_ind;
lim_log(mac_ctx, LOGW,
FL("Delete Station (staId: %d, assocId: %d) "),
msg->staId, msg->assocId);
if ((stads &&
((stads->mlmStaContext.mlmState !=
eLIM_MLM_LINK_ESTABLISHED_STATE) &&
(stads->mlmStaContext.mlmState !=
eLIM_MLM_WT_ASSOC_CNF_STATE) &&
(stads->mlmStaContext.mlmState !=
eLIM_MLM_ASSOCIATED_STATE)))) {
/*
* Received SIR_LIM_DELETE_STA_CONTEXT_IND for STA that
* does not have context or in some transit state.
* Log error
*/
lim_log(mac_ctx, LOGE,
FL("Received SIR_LIM_DELETE_STA_CONTEXT_IND for "
"STA that either has no context or "
"in some transit state, Addr = "
MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(msg->bssId));
cdf_mem_free(msg);
return;
}
stads->mlmStaContext.disassocReason =
eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON;
stads->mlmStaContext.cleanupTrigger =
eLIM_LINK_MONITORING_DEAUTH;
/* Issue Deauth Indication to SME. */
cdf_mem_copy((uint8_t *) &mlm_deauth_ind.peerMacAddr,
stads->staAddr, sizeof(tSirMacAddr));
mlm_deauth_ind.reasonCode =
(uint8_t) stads->mlmStaContext.disassocReason;
mlm_deauth_ind.deauthTrigger =
stads->mlmStaContext.cleanupTrigger;
#ifdef FEATURE_WLAN_TDLS
/* Delete all TDLS peers connected before leaving BSS */
lim_delete_tdls_peers(mac_ctx, session_entry);
#endif
lim_post_sme_message(mac_ctx, LIM_MLM_DEAUTH_IND,
(uint32_t *) &mlm_deauth_ind);
lim_send_sme_deauth_ind(mac_ctx, stads, session_entry);
#ifdef FEATURE_WLAN_TDLS
}
#endif
}
}
/**
* lim_delete_sta_context() - delete sta context.
*
* @mac_ctx: global mac_ctx context
* @lim_msg: lim message.
*
* This function handles the message from HAL: WMA_DELETE_STA_CONTEXT_IND.
* This function validates that the given station id exist, and if so,
* deletes the station by calling lim_trigger_sta_deletion.
*
* Return: none
*/
void lim_delete_sta_context(tpAniSirGlobal mac_ctx, tpSirMsgQ lim_msg)
{
tpDeleteStaContext msg = (tpDeleteStaContext) lim_msg->bodyptr;
tpPESession session_entry;
uint8_t session_id;
if (NULL == msg) {
lim_log(mac_ctx, LOGE, FL("Invalid body pointer in message"));
return;
}
session_entry = pe_find_session_by_bssid(mac_ctx, msg->bssId,
&session_id);
if (NULL == session_entry) {
lim_log(mac_ctx, LOGE, FL("session does not exist"));
cdf_mem_free(msg);
return;
}
switch (msg->reasonCode) {
case HAL_DEL_STA_REASON_CODE_KEEP_ALIVE:
case HAL_DEL_STA_REASON_CODE_TIM_BASED:
lim_delete_sta_util(mac_ctx, msg, session_entry);
break;
case HAL_DEL_STA_REASON_CODE_UNKNOWN_A2:
lim_log(mac_ctx, LOGE, FL("Deleting Unknown station "));
lim_print_mac_addr(mac_ctx, msg->addr2, LOGE);
lim_send_deauth_mgmt_frame(mac_ctx,
eSIR_MAC_CLASS3_FRAME_FROM_NON_ASSOC_STA_REASON,
msg->addr2, session_entry, false);
break;
default:
lim_log(mac_ctx, LOGE, FL(" Unknown reason code "));
break;
}
cdf_mem_free(msg);
return;
}
/**
* lim_trigger_sta_deletion()
*
***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
lim_trigger_sta_deletion(tpAniSirGlobal pMac, tpDphHashNode pStaDs,
tpPESession psessionEntry)
{
tSirSmeDeauthReq *pSmeDeauthReq;
uint8_t *pBuf;
uint8_t *pLen;
uint16_t msgLength = 0;
if (!pStaDs) {
PELOGW(lim_log
(pMac, LOGW, FL("Skip STA deletion (invalid STA)"));
)
return;
}
/**
* MAC based Authentication was used. Trigger
* Deauthentication frame to peer since it will
* take care of disassociation as well.
*/
pSmeDeauthReq = cdf_mem_malloc(sizeof(tSirSmeDeauthReq));
if (NULL == pSmeDeauthReq) {
lim_log(pMac, LOGP,
FL("AllocateMemory failed for eWNI_SME_DEAUTH_REQ "));
return;
}
pBuf = (uint8_t *) &pSmeDeauthReq->messageType;
/* messageType */
lim_copy_u16((uint8_t *) pBuf, eWNI_SME_DISASSOC_REQ);
pBuf += sizeof(uint16_t);
msgLength += sizeof(uint16_t);
/* length */
pLen = pBuf;
pBuf += sizeof(uint16_t);
msgLength += sizeof(uint16_t);
/* sessionId */
*pBuf = psessionEntry->smeSessionId;
pBuf++;
msgLength++;
/* transactionId */
lim_copy_u16((uint8_t *) pBuf, psessionEntry->transactionId);
pBuf += sizeof(uint16_t);
msgLength += sizeof(uint16_t);
/* bssId */
cdf_mem_copy(pBuf, psessionEntry->bssId, sizeof(tSirMacAddr));
pBuf += sizeof(tSirMacAddr);
msgLength += sizeof(tSirMacAddr);
/* peerMacAddr */
cdf_mem_copy(pBuf, pStaDs->staAddr, sizeof(tSirMacAddr));
pBuf += sizeof(tSirMacAddr);
msgLength += sizeof(tSirMacAddr);
/* reasonCode */
lim_copy_u16((uint8_t *) pBuf, (uint16_t) eLIM_LINK_MONITORING_DISASSOC);
pBuf += sizeof(uint16_t);
msgLength += sizeof(uint16_t);
/* Do not send disassoc OTA */
/* pBuf[0] = 1 means do not send the disassoc frame over the air */
/* pBuf[0] = 0 means send the disassoc frame over the air */
pBuf[0] = 0;
pBuf += sizeof(uint8_t);
msgLength += sizeof(uint8_t);
/* Fill in length */
lim_copy_u16((uint8_t *) pLen, msgLength);
lim_post_sme_message(pMac, eWNI_SME_DISASSOC_REQ,
(uint32_t *) pSmeDeauthReq);
cdf_mem_free(pSmeDeauthReq);
} /*** end lim_trigger_st_adeletion() ***/
/**
* lim_tear_down_link_with_ap()
*
***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
lim_tear_down_link_with_ap(tpAniSirGlobal pMac, uint8_t sessionId,
tSirMacReasonCodes reasonCode)
{
tpDphHashNode pStaDs = NULL;
/* tear down the following sessionEntry */
tpPESession psessionEntry;
if ((psessionEntry = pe_find_session_by_session_id(pMac, sessionId)) == NULL) {
lim_log(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.
*/
psessionEntry->pmmOffloadInfo.bcnmiss = false;
lim_log(pMac, LOGW,
FL("No ProbeRsp from AP after HB failure. Tearing down link"));
/* Announce loss of link to Roaming algorithm */
/* and cleanup by sending SME_DISASSOC_REQ to SME */
pStaDs =
dph_get_hash_entry(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 */
lim_delete_tdls_peers(pMac, psessionEntry);
#endif
pStaDs->mlmStaContext.disassocReason = reasonCode;
pStaDs->mlmStaContext.cleanupTrigger =
eLIM_LINK_MONITORING_DEAUTH;
/*
* Set state to mlm State to eLIM_MLM_WT_DEL_STA_RSP_STATE
* This is to address the issue of race condition between
* disconnect request from the HDD and deauth from
* Tx inactivity timer by FWR. This will make sure that we
* will not process disassoc if deauth is in progress for
* the station and thus mlmStaContext.cleanupTrigger will
* not be overwritten.
*/
pStaDs->mlmStaContext.mlmState =
eLIM_MLM_WT_DEL_STA_RSP_STATE;
/* / Issue Deauth Indication to SME. */
cdf_mem_copy((uint8_t *) &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;
lim_log(pMac, LOGE, FL("HB Failure on MAC "
MAC_ADDRESS_STR" Store it on Index %d"),
MAC_ADDR_ARRAY(pStaDs->staAddr),apCount);
sir_copy_mac_addr(pMac->lim.gLimHeartBeatApMac[apCount],
pStaDs->staAddr);
}
mlmDeauthInd.reasonCode =
(uint8_t) pStaDs->mlmStaContext.disassocReason;
mlmDeauthInd.deauthTrigger =
pStaDs->mlmStaContext.cleanupTrigger;
lim_post_sme_message(pMac, LIM_MLM_DEAUTH_IND,
(uint32_t *) &mlmDeauthInd);
lim_send_sme_deauth_ind(pMac, pStaDs, psessionEntry);
}
} /*** lim_tear_down_link_with_ap() ***/
/**
* lim_handle_heart_beat_failure() - handle hear beat failure in STA
*
* @mac_ctx: global MAC context
* @session: PE session entry
*
* This function is called when heartbeat (beacon reception)
* fails on STA
*
* Return: None
*/
void lim_handle_heart_beat_failure(tpAniSirGlobal mac_ctx,
tpPESession session)
{
uint8_t curr_chan;
#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */
host_log_beacon_update_pkt_type *log_ptr = NULL;
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */
WLAN_HOST_DIAG_LOG_ALLOC(log_ptr, host_log_beacon_update_pkt_type,
LOG_WLAN_BEACON_UPDATE_C);
if (log_ptr)
log_ptr->bcn_rx_cnt = session->LimRxedBeaconCntDuringHB;
WLAN_HOST_DIAG_LOG_REPORT(log_ptr);
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
/* Ensure HB Status for the session has been reseted */
session->LimHBFailureStatus = false;
if ((LIM_IS_STA_ROLE(session) ||
LIM_IS_BT_AMP_STA_ROLE(session)) &&
(session->limMlmState == eLIM_MLM_LINK_ESTABLISHED_STATE) &&
(session->limSmeState != eLIM_SME_WT_DISASSOC_STATE) &&
(session->limSmeState != eLIM_SME_WT_DEAUTH_STATE)) {
if (!mac_ctx->sys.gSysEnableLinkMonitorMode)
return;
/* Beacon frame not received within heartbeat timeout. */
lim_log(mac_ctx, LOGW, FL("Heartbeat Failure"));
mac_ctx->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
*/
curr_chan = session->currentOperChannel;
if (!lim_isconnected_on_dfs_channel(curr_chan)) {
/* Detected continuous Beacon Misses */
session->LimHBFailureStatus = true;
/*Reset the HB packet count before sending probe*/
limResetHBPktCount(session);
/**
* Send Probe Request frame to AP to see if
* it is still around. Wait until certain
* timeout for Probe Response from AP.
*/
lim_log(mac_ctx, LOGW,
FL("HB missed from AP. Sending Probe Req"));
/* for searching AP, we don't include any more IE */
lim_send_probe_req_mgmt_frame(mac_ctx, &session->ssId,
session->bssId, curr_chan, session->selfMacAddr,
session->dot11mode, 0, NULL);
} else {
lim_log(mac_ctx, LOGW,
FL("HB missed from AP on DFS chanel moving to passive"));
if (curr_chan < SIR_MAX_24G_5G_CHANNEL_RANGE) {
lim_covert_channel_scan_type(mac_ctx, curr_chan,
false);
mac_ctx->lim.dfschannelList.timeStamp[curr_chan]
= 0;
}
/*
* Connected on DFS channel so should not send the
* probe request tear down the link directly
*/
lim_tear_down_link_with_ap(mac_ctx,
session->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.
*/
lim_log(mac_ctx, LOG1,
FL("received heartbeat timeout in state %X"),
session->limMlmState);
lim_print_mlm_state(mac_ctx, LOG1, session->limMlmState);
mac_ctx->lim.gLimHBfailureCntInOtherStates++;
}
}