blob: 2ab04a0ff3c9c3df4e5efc2fff10bbb77cc112e6 [file] [log] [blame]
/*
* Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
*
* 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.
*/
/**
* DOC: lim_process_mlm_host_roam.c
*
* Host based roaming MLM implementation
*/
#include "cds_api.h"
#include "wni_cfg.h"
#include "ani_global.h"
#include "sir_api.h"
#include "sir_params.h"
#include "sch_api.h"
#include "utils_api.h"
#include "lim_utils.h"
#include "lim_assoc_utils.h"
#include "lim_prop_exts_utils.h"
#include "lim_security_utils.h"
#include "lim_send_messages.h"
#include "lim_send_messages.h"
#include "lim_session_utils.h"
#include <lim_ft.h>
#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM
#include "host_diag_core_log.h"
#endif
#include "wma_if.h"
#include "rrm_api.h"
#include "wma.h"
static void lim_handle_sme_reaasoc_result(struct mac_context *, tSirResultCodes,
uint16_t, struct pe_session *);
/**
* lim_process_mlm_reassoc_req() - process mlm reassoc request.
*
* @mac_ctx: pointer to Global MAC structure
* @msg: pointer to the MLM message buffer
*
* This function is called to process MLM_REASSOC_REQ message
* from SME
*
* Return: None
*/
void lim_process_mlm_reassoc_req(struct mac_context *mac_ctx,
tLimMlmReassocReq *reassoc_req)
{
struct tLimPreAuthNode *auth_node;
tLimMlmReassocCnf reassoc_cnf;
struct pe_session *session;
session = pe_find_session_by_session_id(mac_ctx,
reassoc_req->sessionId);
if (!session) {
pe_err("Session Does not exist for given sessionId: %d",
reassoc_req->sessionId);
qdf_mem_free(reassoc_req);
return;
}
pe_debug("ReAssoc Req on session: %d role: %d mlm: %d " QDF_MAC_ADDR_STR,
reassoc_req->sessionId, GET_LIM_SYSTEM_ROLE(session),
session->limMlmState,
QDF_MAC_ADDR_ARRAY(reassoc_req->peerMacAddr));
if (LIM_IS_AP_ROLE(session) ||
(session->limMlmState !=
eLIM_MLM_LINK_ESTABLISHED_STATE)) {
/*
* Received Reassoc request in invalid state or
* in AP role.Return Reassoc confirm with Invalid
* parameters code.
*/
pe_warn("unexpect msg state: %X role: %d MAC" QDF_MAC_ADDR_STR,
session->limMlmState, GET_LIM_SYSTEM_ROLE(session),
QDF_MAC_ADDR_ARRAY(reassoc_req->peerMacAddr));
lim_print_mlm_state(mac_ctx, LOGW, session->limMlmState);
reassoc_cnf.resultCode = eSIR_SME_INVALID_PARAMETERS;
reassoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;
goto end;
}
if (session->pLimMlmReassocReq)
qdf_mem_free(session->pLimMlmReassocReq);
/*
* Hold Re-Assoc request as part of Session, knock-out mac_ctx
* Hold onto Reassoc request parameters
*/
session->pLimMlmReassocReq = reassoc_req;
/* See if we have pre-auth context with new AP */
auth_node = lim_search_pre_auth_list(mac_ctx, session->limReAssocbssId);
if (!auth_node && (qdf_mem_cmp(reassoc_req->peerMacAddr,
session->bssId,
sizeof(tSirMacAddr)))) {
/*
* Either pre-auth context does not exist AND
* we are not reassociating with currently
* associated AP.
* Return Reassoc confirm with not authenticated
*/
reassoc_cnf.resultCode = eSIR_SME_STA_NOT_AUTHENTICATED;
reassoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;
goto end;
}
/* assign the sessionId to the timer object */
mac_ctx->lim.limTimers.gLimReassocFailureTimer.sessionId =
reassoc_req->sessionId;
session->limPrevMlmState = session->limMlmState;
session->limMlmState = eLIM_MLM_WT_REASSOC_RSP_STATE;
MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, session->peSessionId,
session->limMlmState));
/* Apply previously set configuration at HW */
lim_apply_configuration(mac_ctx, session);
/* store the channel switch reason code in the lim global var */
session->channelChangeReasonCode = LIM_SWITCH_CHANNEL_REASSOC;
mlme_set_chan_switch_in_progress(session->vdev, true);
wlan_vdev_mlme_sm_deliver_evt(session->vdev,
WLAN_VDEV_SM_EV_FW_VDEV_RESTART,
sizeof(struct pe_session), session);
return;
end:
reassoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;
/* Update PE sessio Id */
reassoc_cnf.sessionId = reassoc_req->sessionId;
/* Free up buffer allocated for reassocReq */
qdf_mem_free(reassoc_req);
session->pLimReAssocReq = NULL;
lim_post_sme_message(mac_ctx, LIM_MLM_REASSOC_CNF,
(uint32_t *) &reassoc_cnf);
}
/**
* lim_handle_sme_reaasoc_result() - Handle the reassoc result
* @mac: Global MAC Context
* @resultCode: Result code
* @protStatusCode: Protocol Status Code
* @pe_session: PE Session
*
* This function is called to process reassoc failures
* upon receiving REASSOC_CNF with a failure code or
* MLM_REASSOC_CNF with a success code in case of STA role
*
* Return: None
*/
static void lim_handle_sme_reaasoc_result(struct mac_context *mac,
tSirResultCodes resultCode, uint16_t protStatusCode,
struct pe_session *pe_session)
{
tpDphHashNode sta = NULL;
uint8_t smesessionId;
if (!pe_session) {
pe_err("pe_session is NULL");
return;
}
smesessionId = pe_session->smeSessionId;
if (resultCode != eSIR_SME_SUCCESS) {
sta =
dph_get_hash_entry(mac, DPH_STA_HASH_INDEX_PEER,
&pe_session->dph.dphHashTable);
if (sta) {
sta->mlmStaContext.disassocReason =
eSIR_MAC_UNSPEC_FAILURE_REASON;
sta->mlmStaContext.cleanupTrigger =
eLIM_JOIN_FAILURE;
sta->mlmStaContext.resultCode = resultCode;
sta->mlmStaContext.protStatusCode = protStatusCode;
lim_cleanup_rx_path(mac, sta, pe_session);
/* Cleanup if add bss failed */
if (pe_session->add_bss_failed) {
dph_delete_hash_entry(mac,
sta->staAddr, sta->assocId,
&pe_session->dph.dphHashTable);
goto error;
}
return;
}
}
error:
/* Delete the session if REASSOC failure occurred. */
if (resultCode != eSIR_SME_SUCCESS) {
if (pe_session) {
pe_delete_session(mac, pe_session);
pe_session = NULL;
}
}
lim_send_sme_join_reassoc_rsp(mac, eWNI_SME_REASSOC_RSP, resultCode,
protStatusCode, pe_session, smesessionId);
}
/**
* lim_process_mlm_reassoc_cnf() - process mlm reassoc cnf msg
*
* @mac_ctx: Pointer to Global MAC structure
* @msg_buf: A pointer to the MLM message buffer
*
* This function is called to process MLM_REASSOC_CNF message from MLM State
* machine.
*
* @Return: void
*/
void lim_process_mlm_reassoc_cnf(struct mac_context *mac_ctx, uint32_t *msg_buf)
{
struct pe_session *session;
tLimMlmReassocCnf *lim_mlm_reassoc_cnf;
struct reassoc_params param;
QDF_STATUS status;
if (!msg_buf) {
pe_err("Buffer is Pointing to NULL");
return;
}
lim_mlm_reassoc_cnf = (tLimMlmReassocCnf *)msg_buf;
session = pe_find_session_by_session_id(
mac_ctx,
lim_mlm_reassoc_cnf->sessionId);
if (!session) {
pe_err("session Does not exist for given session Id");
return;
}
if (session->limSmeState != eLIM_SME_WT_REASSOC_STATE ||
LIM_IS_AP_ROLE(session)) {
/*
* Should not have received Reassocication confirm
* from MLM in other states OR on AP.
*/
pe_err("Rcv unexpected MLM_REASSOC_CNF role: %d sme 0x%X",
GET_LIM_SYSTEM_ROLE(session), session->limSmeState);
return;
}
/*
* Upon Reassoc success or failure, freeup the cached preauth request,
* to ensure that channel switch is now allowed following any change in
* HT params.
*/
if (session->ftPEContext.pFTPreAuthReq) {
pe_debug("Freeing pFTPreAuthReq: %pK",
session->ftPEContext.pFTPreAuthReq);
if (session->ftPEContext.pFTPreAuthReq->pbssDescription) {
qdf_mem_free(
session->ftPEContext.pFTPreAuthReq->pbssDescription);
session->ftPEContext.pFTPreAuthReq->pbssDescription =
NULL;
}
qdf_mem_free(session->ftPEContext.pFTPreAuthReq);
session->ftPEContext.pFTPreAuthReq = NULL;
session->ftPEContext.ftPreAuthSession = false;
}
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
if (session->bRoamSynchInProgress) {
pe_debug("LFR3:Re-set the LIM Ctxt Roam Synch In Progress");
session->bRoamSynchInProgress = false;
}
#endif
pe_debug("Rcv MLM_REASSOC_CNF with result code: %d",
lim_mlm_reassoc_cnf->resultCode);
if (lim_mlm_reassoc_cnf->resultCode == eSIR_SME_SUCCESS) {
/* Successful Reassociation */
pe_debug("*** Reassociated with new BSS ***");
session->limSmeState = eLIM_SME_LINK_EST_STATE;
MTRACE(mac_trace(
mac_ctx, TRACE_CODE_SME_STATE,
session->peSessionId, session->limSmeState));
wlan_vdev_mlme_sm_deliver_evt(session->vdev,
WLAN_VDEV_SM_EV_START_SUCCESS,
0, NULL);
/* Need to send Reassoc rsp with Reassoc success to Host. */
lim_send_sme_join_reassoc_rsp(
mac_ctx, eWNI_SME_REASSOC_RSP,
lim_mlm_reassoc_cnf->resultCode,
lim_mlm_reassoc_cnf->protStatusCode,
session, session->smeSessionId);
} else {
param.result_code = lim_mlm_reassoc_cnf->resultCode;
param.prot_status_code = lim_mlm_reassoc_cnf->protStatusCode;
param.session = session;
mlme_set_connection_fail(session->vdev, true);
if (wlan_vdev_mlme_get_substate(session->vdev) ==
WLAN_VDEV_SS_START_START_PROGRESS)
status = wlan_vdev_mlme_sm_deliver_evt(session->vdev,
WLAN_VDEV_SM_EV_START_REQ_FAIL,
sizeof(param), &param);
else
status = wlan_vdev_mlme_sm_deliver_evt(session->vdev,
WLAN_VDEV_SM_EV_CONNECTION_FAIL,
sizeof(param), &param);
}
if (session->pLimReAssocReq) {
qdf_mem_free(session->pLimReAssocReq);
session->pLimReAssocReq = NULL;
}
}
QDF_STATUS lim_sta_reassoc_error_handler(struct reassoc_params *param)
{
struct pe_session *session;
struct mac_context *mac_ctx;
if (!param) {
pe_err("param is NULL");
return QDF_STATUS_E_INVAL;
}
mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
if (!mac_ctx) {
pe_err("mac_ctx is NULL");
return QDF_STATUS_E_INVAL;
}
session = param->session;
if (param->result_code
== eSIR_SME_REASSOC_REFUSED) {
/*
* Reassociation failure With the New AP but we still have the
* link with the Older AP
*/
session->limSmeState = eLIM_SME_LINK_EST_STATE;
MTRACE(mac_trace(
mac_ctx, TRACE_CODE_SME_STATE,
session->peSessionId, session->limSmeState));
/* Need to send Reassoc rsp with Assoc failure to Host. */
lim_send_sme_join_reassoc_rsp(
mac_ctx, eWNI_SME_REASSOC_RSP,
param->result_code,
param->prot_status_code,
session, session->smeSessionId);
} else {
/* Reassociation failure */
session->limSmeState = eLIM_SME_JOIN_FAILURE_STATE;
MTRACE(mac_trace(
mac_ctx, TRACE_CODE_SME_STATE,
session->peSessionId, session->limSmeState));
/* Need to send Reassoc rsp with Assoc failure to Host. */
lim_handle_sme_reaasoc_result(
mac_ctx,
param->result_code,
param->prot_status_code,
session);
}
return QDF_STATUS_SUCCESS;
}
void lim_process_sta_mlm_add_bss_rsp_ft(struct mac_context *mac,
struct add_bss_rsp *add_bss_rsp,
struct pe_session *pe_session)
{
tLimMlmReassocCnf mlmReassocCnf; /* keep sme */
tpDphHashNode sta = NULL;
tpAddStaParams pAddStaParams = NULL;
uint32_t listenInterval = MLME_CFG_LISTEN_INTERVAL;
uint32_t selfStaDot11Mode = 0;
/* Sanity Checks */
if (!add_bss_rsp) {
pe_err("add_bss_rsp is NULL");
goto end;
}
if (eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE !=
pe_session->limMlmState) {
goto end;
}
sta = dph_add_hash_entry(mac, pe_session->bssId,
DPH_STA_HASH_INDEX_PEER,
&pe_session->dph.dphHashTable);
if (!sta) {
/* Could not add hash table entry */
pe_err("could not add hash entry at DPH for %pM",
pe_session->bssId);
goto end;
}
/* Prepare and send Reassociation request frame */
/* start reassoc timer. */
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
if (pe_session->bRoamSynchInProgress != true) {
#endif
mac->lim.limTimers.gLimReassocFailureTimer.sessionId =
pe_session->peSessionId;
/* / Start reassociation failure timer */
MTRACE(mac_trace
(mac, TRACE_CODE_TIMER_ACTIVATE,
pe_session->peSessionId, eLIM_REASSOC_FAIL_TIMER));
if (tx_timer_activate
(&mac->lim.limTimers.gLimReassocFailureTimer)
!= TX_SUCCESS) {
/* / Could not start reassoc failure timer. */
/* Log error */
pe_err("could not start Reassoc failure timer");
/* Return Reassoc confirm with */
/* Resources Unavailable */
mlmReassocCnf.resultCode =
eSIR_SME_RESOURCES_UNAVAILABLE;
mlmReassocCnf.protStatusCode =
eSIR_MAC_UNSPEC_FAILURE_STATUS;
goto end;
}
mac->lim.pe_session = pe_session;
if (!mac->lim.pe_session->pLimMlmReassocRetryReq) {
/* Take a copy of reassoc request for retrying */
mac->lim.pe_session->pLimMlmReassocRetryReq =
qdf_mem_malloc(sizeof(tLimMlmReassocReq));
if (!mac->lim.pe_session->pLimMlmReassocRetryReq)
goto end;
qdf_mem_copy(mac->lim.pe_session->
pLimMlmReassocRetryReq,
pe_session->pLimMlmReassocReq,
sizeof(tLimMlmReassocReq));
}
mac->lim.reAssocRetryAttempt = 0;
lim_send_reassoc_req_with_ft_ies_mgmt_frame(mac,
pe_session->
pLimMlmReassocReq,
pe_session);
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
} else {
pe_debug("LFR3:Do not activate timer and dont send the reassoc");
}
#endif
pe_session->limPrevMlmState = pe_session->limMlmState;
pe_session->limMlmState = eLIM_MLM_WT_FT_REASSOC_RSP_STATE;
MTRACE(mac_trace
(mac, TRACE_CODE_MLM_STATE, pe_session->peSessionId,
eLIM_MLM_WT_FT_REASSOC_RSP_STATE));
pe_debug("Set the mlm state: %d session: %d",
pe_session->limMlmState, pe_session->peSessionId);
pe_session->bss_idx = add_bss_rsp->vdev_id;
/* Success, handle below */
/* STA Index(genr by HAL) for the BSS entry is stored here */
sta->staIndex = wma_peer_get_peet_id(pe_session->bssId);
pAddStaParams = qdf_mem_malloc(sizeof(tAddStaParams));
if (!pAddStaParams)
goto end;
/* / Add STA context at MAC HW (BMU, RHP & TFP) */
qdf_mem_copy((uint8_t *)pAddStaParams->staMac,
(uint8_t *)pe_session->self_mac_addr,
sizeof(tSirMacAddr));
pAddStaParams->staType = STA_ENTRY_SELF;
pAddStaParams->status = QDF_STATUS_SUCCESS;
/* Update PE session ID */
pAddStaParams->sessionId = pe_session->peSessionId;
pAddStaParams->smesessionId = pe_session->smeSessionId;
/* This will indicate HAL to "allocate" a new STA index */
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
if (pe_session->bRoamSynchInProgress != true)
#endif
pAddStaParams->staIdx = STA_INVALID_IDX;
pAddStaParams->updateSta = false;
lim_populate_peer_rate_set(mac, &pAddStaParams->supportedRates, NULL,
false, pe_session, NULL, NULL);
if (pe_session->htCapability) {
pAddStaParams->htCapable = pe_session->htCapability;
pAddStaParams->vhtCapable = pe_session->vhtCapability;
pAddStaParams->ch_width = pe_session->ch_width;
pAddStaParams->mimoPS =
lim_get_ht_capability(mac, eHT_MIMO_POWER_SAVE,
pe_session);
pAddStaParams->maxAmpduDensity =
lim_get_ht_capability(mac, eHT_MPDU_DENSITY,
pe_session);
pAddStaParams->maxAmpduSize =
lim_get_ht_capability(mac, eHT_MAX_RX_AMPDU_FACTOR,
pe_session);
pAddStaParams->fShortGI20Mhz =
lim_get_ht_capability(mac, eHT_SHORT_GI_20MHZ,
pe_session);
pAddStaParams->fShortGI40Mhz =
lim_get_ht_capability(mac, eHT_SHORT_GI_40MHZ,
pe_session);
}
listenInterval = mac->mlme_cfg->sap_cfg.listen_interval;
pAddStaParams->listenInterval = (uint16_t) listenInterval;
selfStaDot11Mode = mac->mlme_cfg->dot11_mode.dot11_mode;
pAddStaParams->encryptType = pe_session->encryptType;
pAddStaParams->maxTxPower = pe_session->maxTxPower;
/* Lets save this for when we receive the Reassoc Rsp */
pe_session->ftPEContext.pAddStaReq = pAddStaParams;
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
if (pe_session->bRoamSynchInProgress) {
pe_debug("LFR3:Prep and save AddStaReq for post-assoc-rsp");
lim_process_assoc_rsp_frame(mac, mac->roam.pReassocResp,
LIM_REASSOC, pe_session);
}
#endif
return;
end:
/* Free up buffer allocated for reassocReq */
if (pe_session)
if (pe_session->pLimMlmReassocReq) {
qdf_mem_free(pe_session->pLimMlmReassocReq);
pe_session->pLimMlmReassocReq = NULL;
}
mlmReassocCnf.resultCode = eSIR_SME_FT_REASSOC_FAILURE;
mlmReassocCnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;
/* Update PE session Id */
if (pe_session)
mlmReassocCnf.sessionId = pe_session->peSessionId;
else
mlmReassocCnf.sessionId = 0;
lim_post_sme_message(mac, LIM_MLM_REASSOC_CNF,
(uint32_t *) &mlmReassocCnf);
}
void lim_process_mlm_ft_reassoc_req(struct mac_context *mac,
tLimMlmReassocReq *reassoc_req)
{
struct pe_session *session;
uint16_t caps;
uint32_t val;
QDF_STATUS status;
uint32_t teleBcnEn = 0;
if (!reassoc_req) {
pe_err("reassoc_req is NULL");
return;
}
session = pe_find_session_by_session_id(mac, reassoc_req->sessionId);
if (!session) {
pe_err("session Does not exist for given session Id");
qdf_mem_free(reassoc_req);
return;
}
#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM /* FEATURE_WLAN_DIAG_SUPPORT */
lim_diag_event_report(mac, WLAN_PE_DIAG_REASSOCIATING,
session, 0, 0);
#endif
/* Nothing to be done if the session is not in STA mode */
if (!LIM_IS_STA_ROLE(session)) {
pe_err("pe_session is not in STA mode");
qdf_mem_free(reassoc_req);
return;
}
if (!session->ftPEContext.pAddBssReq) {
pe_err("pAddBssReq is NULL");
return;
}
qdf_mem_copy(reassoc_req->peerMacAddr,
session->bssId, sizeof(tSirMacAddr));
if (lim_get_capability_info(mac, &caps, session) !=
QDF_STATUS_SUCCESS) {
/**
* Could not get Capabilities value
* from CFG. Log error.
*/
pe_err("could not get Capabilities value");
qdf_mem_free(reassoc_req);
return;
}
lim_update_caps_info_for_bss(mac, &caps,
session->pLimReAssocReq->bssDescription.capabilityInfo);
pe_debug("Capabilities info FT Reassoc: 0x%X", caps);
reassoc_req->capabilityInfo = caps;
/* If telescopic beaconing is enabled, set listen interval
to CFG_TELE_BCN_MAX_LI
*/
teleBcnEn = mac->mlme_cfg->sap_cfg.tele_bcn_wakeup_en;
if (teleBcnEn)
val = mac->mlme_cfg->sap_cfg.tele_bcn_max_li;
else
val = mac->mlme_cfg->sap_cfg.listen_interval;
status = wma_add_bss_peer_sta(session->self_mac_addr, session->bssId,
false);
if (QDF_IS_STATUS_ERROR(status)) {
qdf_mem_free(reassoc_req);
return;
}
reassoc_req->listenInterval = (uint16_t) val;
session->pLimMlmReassocReq = reassoc_req;
/* we need to defer the message until we get response back from HAL */
SET_LIM_PROCESS_DEFD_MESGS(mac, false);
status = wma_add_bss_lfr2_vdev_start(session->ftPEContext.pAddBssReq);
if (QDF_IS_STATUS_ERROR(status)) {
SET_LIM_PROCESS_DEFD_MESGS(mac, true);
pe_err("wma_add_bss_lfr2_vdev_start, reason: %X",
status);
}
qdf_mem_free(session->ftPEContext.pAddBssReq);
session->ftPEContext.pAddBssReq = NULL;
return;
}