| /* |
| * 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++; |
| } |
| } |