blob: ee161f338c32b8942ed40f42faca380479550e77 [file] [log] [blame]
/*
* Copyright (c) 2012-2018 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 sch_beacon_process.cc contains beacon processing related
* functions
*
* Author: Sandesh Goel
* Date: 02/25/02
* History:-
* Date Modified by Modification Information
* --------------------------------------------------------------------
*
*/
#include "cds_api.h"
#include "wni_cfg.h"
#include "cfg_api.h"
#include "lim_api.h"
#include "utils_api.h"
#include "sch_api.h"
#include "lim_utils.h"
#include "lim_send_messages.h"
#include "lim_sta_hash_api.h"
#include "rrm_api.h"
#ifdef FEATURE_WLAN_DIAG_SUPPORT
#include "host_diag_core_log.h"
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
#include "wma.h"
/**
* Number of bytes of variation in beacon length from the last beacon
* to trigger reprogramming of rx delay register
*/
#define SCH_BEACON_LEN_DELTA 3
/* calculate 2^cw - 1 */
#define CW_GET(cw) (((cw) == 0) ? 1 : ((1 << (cw)) - 1))
static void
ap_beacon_process_5_ghz(tpAniSirGlobal mac_ctx, uint8_t *rx_pkt_info,
tpSchBeaconStruct bcn_struct,
tpUpdateBeaconParams bcn_prm, tpPESession session,
uint32_t phy_mode)
{
tpSirMacMgmtHdr mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info);
if (!session->htCapability)
return;
if (bcn_struct->channelNumber != session->currentOperChannel)
return;
/* 11a (non HT) AP overlaps or */
/* HT AP with HT op mode as mixed overlaps. */
/* HT AP with HT op mode as overlap legacy overlaps. */
if (!bcn_struct->HTInfo.present
|| (eSIR_HT_OP_MODE_MIXED == bcn_struct->HTInfo.opMode)
|| (eSIR_HT_OP_MODE_OVERLAP_LEGACY == bcn_struct->HTInfo.opMode)) {
lim_update_overlap_sta_param(mac_ctx, mac_hdr->bssId,
&(session->gLimOverlap11aParams));
if (session->gLimOverlap11aParams.numSta
&& !session->gLimOverlap11aParams.protectionEnabled) {
lim_update_11a_protection(mac_ctx, true, true,
bcn_prm, session);
}
return;
}
/* HT AP with HT20 op mode overlaps. */
if (eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT != bcn_struct->HTInfo.opMode)
return;
lim_update_overlap_sta_param(mac_ctx, mac_hdr->bssId,
&(session->gLimOverlapHt20Params));
if (session->gLimOverlapHt20Params.numSta
&& !session->gLimOverlapHt20Params.protectionEnabled)
lim_enable_ht20_protection(mac_ctx, true, true,
bcn_prm, session);
}
static void
ap_beacon_process_24_ghz(tpAniSirGlobal mac_ctx, uint8_t *rx_pkt_info,
tpSchBeaconStruct bcn_struct,
tpUpdateBeaconParams bcn_prm, tpPESession session,
uint32_t phy_mode)
{
tpSirMacMgmtHdr mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info);
bool tmp_exp = false;
/* We are 11G AP. */
if ((phy_mode == WNI_CFG_PHY_MODE_11G) &&
(false == session->htCapability)) {
if (bcn_struct->channelNumber != session->currentOperChannel)
return;
tmp_exp = (!bcn_struct->erpPresent
&& !bcn_struct->HTInfo.present)
/* if erp not present then 11B AP overlapping */
|| (!mac_ctx->roam.configParam.ignore_peer_erp_info &&
bcn_struct->erpPresent &&
(bcn_struct->erpIEInfo.useProtection
|| bcn_struct->erpIEInfo.nonErpPresent));
if (!tmp_exp)
return;
#ifdef FEATURE_WLAN_ESE
if (session->isESEconnection)
QDF_TRACE(QDF_MODULE_ID_PE,
QDF_TRACE_LEVEL_INFO,
FL("[INFOLOG]ESE 11g erpPresent=%d useProtection=%d nonErpPresent=%d"),
bcn_struct->erpPresent,
bcn_struct->erpIEInfo.useProtection,
bcn_struct->erpIEInfo.nonErpPresent);
#endif
lim_enable_overlap11g_protection(mac_ctx, bcn_prm,
mac_hdr, session);
return;
}
/* handling the case when HT AP has overlapping legacy BSS. */
if (!session->htCapability)
return;
if (bcn_struct->channelNumber != session->currentOperChannel)
return;
tmp_exp = (!bcn_struct->erpPresent && !bcn_struct->HTInfo.present)
/* if erp not present then 11B AP overlapping */
|| (!mac_ctx->roam.configParam.ignore_peer_erp_info &&
bcn_struct->erpPresent &&
(bcn_struct->erpIEInfo.useProtection
|| bcn_struct->erpIEInfo.nonErpPresent));
if (tmp_exp) {
#ifdef FEATURE_WLAN_ESE
if (session->isESEconnection) {
QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_INFO,
FL("[INFOLOG]ESE 11g erpPresent=%d useProtection=%d nonErpPresent=%d"),
bcn_struct->erpPresent,
bcn_struct->erpIEInfo.useProtection,
bcn_struct->erpIEInfo.nonErpPresent);
}
#endif
lim_enable_overlap11g_protection(mac_ctx, bcn_prm,
mac_hdr, session);
}
/* 11g device overlaps */
tmp_exp = bcn_struct->erpPresent
&& !(bcn_struct->erpIEInfo.useProtection
|| bcn_struct->erpIEInfo.nonErpPresent)
&& !(bcn_struct->HTInfo.present);
if (tmp_exp) {
lim_update_overlap_sta_param(mac_ctx, mac_hdr->bssId,
&(session->gLimOverlap11gParams));
if (session->gLimOverlap11gParams.numSta
&& !session->gLimOverlap11gParams.protectionEnabled)
lim_enable_ht_protection_from11g(mac_ctx, true, true,
bcn_prm, session);
}
/* ht device overlaps.
* here we will check for HT related devices only which might need
* protection. check for 11b and 11g is already done in the previous
* blocks. so we will not check for HT operating mode as MIXED.
*/
if (!bcn_struct->HTInfo.present)
return;
/*
* if we are not already in mixed mode or legacy mode as HT operating
* mode and received beacon has HT operating mode as legacy then we need
* to enable protection from 11g station. we don't need protection from
* 11b because if that's needed then our operating mode would have
* already been set to legacy in the previous blocks.
*/
if ((eSIR_HT_OP_MODE_OVERLAP_LEGACY == bcn_struct->HTInfo.opMode) &&
!mac_ctx->roam.configParam.ignore_peer_ht_opmode) {
if (eSIR_HT_OP_MODE_OVERLAP_LEGACY == mac_ctx->lim.gHTOperMode
|| eSIR_HT_OP_MODE_MIXED == mac_ctx->lim.gHTOperMode)
return;
lim_update_overlap_sta_param(mac_ctx, mac_hdr->bssId,
&(session->gLimOverlap11gParams));
if (session->gLimOverlap11gParams.numSta
&& !session->gLimOverlap11gParams.protectionEnabled)
lim_enable_ht_protection_from11g(mac_ctx, true, true,
bcn_prm, session);
return;
}
if (eSIR_HT_OP_MODE_NO_LEGACY_20MHZ_HT == bcn_struct->HTInfo.opMode) {
lim_update_overlap_sta_param(mac_ctx, mac_hdr->bssId,
&(session->gLimOverlapHt20Params));
if (session->gLimOverlapHt20Params.numSta
&& !session->gLimOverlapHt20Params.protectionEnabled)
lim_enable_ht20_protection(mac_ctx, true, true,
bcn_prm, session);
}
}
/**
* ap_beacon_process() - processes incoming beacons
*
* @mac_ctx: mac global context
* @rx_pkt_info: incoming beacon packet
* @bcn_struct: beacon struct
* @bcn_prm: beacon params
* @session: pe session entry
*
* Return: void
*/
static void
ap_beacon_process(tpAniSirGlobal mac_ctx, uint8_t *rx_pkt_info,
tpSchBeaconStruct bcn_struct,
tpUpdateBeaconParams bcn_prm, tpPESession session)
{
uint32_t phy_mode;
enum band_info rf_band = BAND_UNKNOWN;
/* Get RF band from session */
rf_band = session->limRFBand;
lim_get_phy_mode(mac_ctx, &phy_mode, session);
if (BAND_5G == rf_band)
ap_beacon_process_5_ghz(mac_ctx, rx_pkt_info, bcn_struct,
bcn_prm, session, phy_mode);
else if (BAND_2G == rf_band)
ap_beacon_process_24_ghz(mac_ctx, rx_pkt_info, bcn_struct,
bcn_prm, session, phy_mode);
}
/* -------------------------------------------------------------------- */
/**
* __sch_beacon_process_no_session
*
* FUNCTION:
* Process the received beacon frame when
* -- Station is not scanning
* -- No corresponding session is found
*
* LOGIC:
* Following scenarios exist when Session Does not exist:
* * IBSS Beacons, when IBSS session already exists with same SSID,
* but from STA which has not yet joined and has a different BSSID.
* - invoke lim_handle_ibs_scoalescing with the session context of existing IBSS session.
*
* * IBSS Beacons when IBSS session does not exist, only Infra or BT-AMP session exists,
* then save the beacon in the scan results and throw it away.
*
* * Infra Beacons
* - beacons received when no session active
* should not come here, it should be handled as part of scanning,
* else they should not be getting received, should update scan results and drop it if that happens.
* - beacons received when IBSS session active:
* update scan results and drop it.
* - beacons received when Infra session(STA) is active:
* update scan results and drop it
* - beacons received when BT-STA session is active:
* update scan results and drop it.
* - beacons received when Infra/BT-STA or Infra/IBSS is active.
* update scan results and drop it.
*
*/
static void __sch_beacon_process_no_session(tpAniSirGlobal pMac,
tpSchBeaconStruct pBeacon,
uint8_t *pRxPacketInfo)
{
tpPESession psessionEntry = NULL;
psessionEntry = lim_is_ibss_session_active(pMac);
if (psessionEntry != NULL) {
lim_handle_ibss_coalescing(pMac, pBeacon, pRxPacketInfo,
psessionEntry);
}
return;
}
/**
* get_operating_channel_width() - Get operating channel width
* @stads - station entry.
*
* This function returns the operating channel width based on
* the supported channel width entry.
*
* Return: tSirMacHTChannelWidth on success
*/
static tSirMacHTChannelWidth get_operating_channel_width(tpDphHashNode stads)
{
tSirMacHTChannelWidth ch_width = eHT_CHANNEL_WIDTH_20MHZ;
if (stads->vhtSupportedChannelWidthSet ==
WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ)
ch_width = eHT_CHANNEL_WIDTH_160MHZ;
else if (stads->vhtSupportedChannelWidthSet ==
WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ)
ch_width = eHT_CHANNEL_WIDTH_160MHZ;
else if (stads->vhtSupportedChannelWidthSet ==
WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ)
ch_width = eHT_CHANNEL_WIDTH_80MHZ;
else if (stads->htSupportedChannelWidthSet)
ch_width = eHT_CHANNEL_WIDTH_40MHZ;
else
ch_width = eHT_CHANNEL_WIDTH_20MHZ;
return ch_width;
}
/*
* sch_bcn_process_sta() - Process the received beacon frame for sta,
* bt_amp_sta
*
* @mac_ctx: mac_ctx
* @bcn: beacon struct
* @rx_pkt_info: received packet info
* @session: pe session pointer
* @bssIdx: bss index
* @beaconParams: update beacon params
* @sendProbeReq: out flag to indicate if probe rsp is to be sent
* @pMh: mac header
*
* Process the received beacon frame for sta
*
* Return: success of failure of operation
*/
static bool
sch_bcn_process_sta(tpAniSirGlobal mac_ctx,
tpSchBeaconStruct bcn,
uint8_t *rx_pkt_info,
tpPESession session, uint8_t *bssIdx,
tUpdateBeaconParams *beaconParams,
uint8_t *sendProbeReq, tpSirMacMgmtHdr pMh)
{
uint32_t bi;
tpDphHashNode pStaDs = NULL;
/*
* This handles two cases:
* -- Infra STA receving beacons from AP
*/
/**
* This is the Beacon received from the AP we're currently associated
* with. Check if there are any changes in AP's capabilities
*/
if ((uint8_t) bcn->channelNumber != session->currentOperChannel) {
pe_err("Channel Change from %d --> %d - Ignoring beacon!",
session->currentOperChannel,
bcn->channelNumber);
return false;
}
lim_detect_change_in_ap_capabilities(mac_ctx, bcn, session);
if (lim_get_sta_hash_bssidx(mac_ctx, DPH_STA_HASH_INDEX_PEER, bssIdx,
session) != eSIR_SUCCESS)
return false;
beaconParams->bssIdx = *bssIdx;
qdf_mem_copy((uint8_t *) &session->lastBeaconTimeStamp,
(uint8_t *) bcn->timeStamp, sizeof(uint64_t));
session->currentBssBeaconCnt++;
if (session->bcon_dtim_period != bcn->tim.dtimPeriod) {
session->bcon_dtim_period = bcn->tim.dtimPeriod;
lim_send_set_dtim_period(mac_ctx, bcn->tim.dtimPeriod,
session);
}
MTRACE(mac_trace(mac_ctx, TRACE_CODE_RX_MGMT_TSF,
session->peSessionId, bcn->timeStamp[0]));
MTRACE(mac_trace(mac_ctx, TRACE_CODE_RX_MGMT_TSF,
session->peSessionId, bcn->timeStamp[1]));
/* Read beacon interval session Entry */
bi = session->beaconParams.beaconInterval;
if (bi != bcn->beaconInterval) {
pe_debug("Beacon interval changed from %d to %d",
bcn->beaconInterval, bi);
bi = bcn->beaconInterval;
session->beaconParams.beaconInterval = (uint16_t) bi;
beaconParams->paramChangeBitmap |= PARAM_BCN_INTERVAL_CHANGED;
beaconParams->beaconInterval = (uint16_t) bi;
}
if (bcn->cfPresent) {
cfg_set_int(mac_ctx, WNI_CFG_CFP_PERIOD,
bcn->cfParamSet.cfpPeriod);
lim_send_cf_params(mac_ctx, *bssIdx,
bcn->cfParamSet.cfpCount,
bcn->cfParamSet.cfpPeriod);
}
/* No need to send DTIM Period and Count to HAL/SMAC */
/* SMAC already parses TIM bit. */
if (bcn->timPresent)
cfg_set_int(mac_ctx, WNI_CFG_DTIM_PERIOD, bcn->tim.dtimPeriod);
if (mac_ctx->lim.gLimProtectionControl !=
WNI_CFG_FORCE_POLICY_PROTECTION_DISABLE)
lim_decide_sta_protection(mac_ctx, bcn, beaconParams, session);
if (bcn->erpPresent) {
if (bcn->erpIEInfo.barkerPreambleMode)
lim_enable_short_preamble(mac_ctx, false,
beaconParams, session);
else
lim_enable_short_preamble(mac_ctx, true,
beaconParams, session);
}
lim_update_short_slot(mac_ctx, bcn, beaconParams, session);
pStaDs = dph_get_hash_entry(mac_ctx, DPH_STA_HASH_INDEX_PEER,
&session->dph.dphHashTable);
if ((bcn->wmeEdcaPresent && (session->limWmeEnabled))
|| (bcn->edcaPresent && (session->limQosEnabled))) {
if (bcn->edcaParams.qosInfo.count !=
session->gLimEdcaParamSetCount) {
if (sch_beacon_edca_process(mac_ctx, &bcn->edcaParams,
session) != eSIR_SUCCESS) {
pe_err("EDCA parameter processing error");
} else if (pStaDs != NULL) {
/* If needed, downgrade the EDCA parameters */
lim_set_active_edca_params(mac_ctx,
session->gLimEdcaParams, session);
lim_send_edca_params(mac_ctx,
session->gLimEdcaParamsActive,
pStaDs->bssId);
} else {
pe_err("Self Entry missing in Hash Table");
}
}
return true;
}
if ((bcn->qosCapabilityPresent && session->limQosEnabled)
&& (bcn->qosCapability.qosInfo.count !=
session->gLimEdcaParamSetCount))
*sendProbeReq = true;
return true;
}
/**
* update_nss() - Function to update NSS
* @mac_ctx: pointer to Global Mac structure
* @sta_ds: pointer to tpDphHashNode
* @beacon: pointer to tpSchBeaconStruct
* @session_entry: pointer to tpPESession
* @mgmt_hdr: pointer to tpSirMacMgmtHdr
*
* function to update NSS
*
* Return: none
*/
static void update_nss(tpAniSirGlobal mac_ctx, tpDphHashNode sta_ds,
tpSchBeaconStruct beacon, tpPESession session_entry,
tpSirMacMgmtHdr mgmt_hdr)
{
if (sta_ds->vhtSupportedRxNss != (beacon->OperatingMode.rxNSS + 1)) {
sta_ds->vhtSupportedRxNss =
beacon->OperatingMode.rxNSS + 1;
lim_set_nss_change(mac_ctx, session_entry,
sta_ds->vhtSupportedRxNss, sta_ds->staIndex,
mgmt_hdr->sa);
}
}
#ifdef WLAN_FEATURE_11AX_BSS_COLOR
static void
sch_bcn_update_he_ies(tpAniSirGlobal mac_ctx, tpDphHashNode sta_ds,
tpPESession session, tpSchBeaconStruct bcn,
tpSirMacMgmtHdr mac_hdr)
{
uint8_t session_bss_col_disabled_flag;
bool anything_changed = false;
if (session->is_session_obss_color_collision_det_enabled)
return;
if (session->he_op.present && bcn->he_op.present) {
if (bcn->vendor_he_bss_color_change.present &&
(session->he_op.bss_color !=
bcn->vendor_he_bss_color_change.new_color)) {
pe_debug("bss color changed from [%d] to [%d]",
session->he_op.bss_color,
bcn->vendor_he_bss_color_change.new_color);
session->he_op.bss_color =
bcn->vendor_he_bss_color_change.new_color;
anything_changed = true;
}
session_bss_col_disabled_flag = session->he_op.bss_col_disabled;
if (session_bss_col_disabled_flag !=
bcn->he_op.bss_col_disabled) {
pe_debug("color disable flag changed from [%d] to [%d]",
session->he_op.bss_col_disabled,
bcn->he_op.bss_col_disabled);
session->he_op.bss_col_disabled =
bcn->he_op.bss_col_disabled;
anything_changed = true;
}
}
if (anything_changed)
lim_send_he_ie_update(mac_ctx, session);
}
#else
static void
sch_bcn_update_he_ies(tpAniSirGlobal mac_ctx, tpDphHashNode sta_ds,
tpPESession session, tpSchBeaconStruct bcn,
tpSirMacMgmtHdr mac_hdr)
{
return;
}
#endif
static void
sch_bcn_update_opmode_change(tpAniSirGlobal mac_ctx, tpDphHashNode sta_ds,
tpPESession session, tpSchBeaconStruct bcn,
tpSirMacMgmtHdr mac_hdr, uint8_t cb_mode)
{
bool skip_opmode_update = false;
uint8_t oper_mode;
uint32_t fw_vht_ch_wd = wma_get_vht_ch_width();
uint8_t ch_width = 0;
if (session->vhtCapability && bcn->OperatingMode.present) {
update_nss(mac_ctx, sta_ds, bcn, session, mac_hdr);
oper_mode = get_operating_channel_width(sta_ds);
if ((oper_mode == eHT_CHANNEL_WIDTH_80MHZ) &&
(bcn->OperatingMode.chanWidth > eHT_CHANNEL_WIDTH_80MHZ))
skip_opmode_update = true;
if (WNI_CFG_CHANNEL_BONDING_MODE_DISABLE == cb_mode) {
/*
* if channel bonding is disabled from INI and
* receiving beacon which has operating mode IE
* containing channel width change then don't update
* CH_WIDTH
*/
pe_err("CB disabled & CH_WIDTH changed old[%d] new[%d]",
oper_mode, bcn->OperatingMode.chanWidth);
return;
}
if (!skip_opmode_update &&
((oper_mode != bcn->OperatingMode.chanWidth) ||
(sta_ds->vhtSupportedRxNss !=
(bcn->OperatingMode.rxNSS + 1)))) {
pe_debug("received OpMode Chanwidth %d, staIdx = %d",
bcn->OperatingMode.chanWidth, sta_ds->staIndex);
pe_debug("MAC - %0x:%0x:%0x:%0x:%0x:%0x",
mac_hdr->sa[0], mac_hdr->sa[1],
mac_hdr->sa[2], mac_hdr->sa[3],
mac_hdr->sa[4], mac_hdr->sa[5]);
if ((bcn->OperatingMode.chanWidth >=
eHT_CHANNEL_WIDTH_160MHZ) &&
(fw_vht_ch_wd > eHT_CHANNEL_WIDTH_80MHZ)) {
pe_debug("Updating the CH Width to 160MHz");
sta_ds->vhtSupportedChannelWidthSet =
WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ;
sta_ds->htSupportedChannelWidthSet =
eHT_CHANNEL_WIDTH_40MHZ;
ch_width = eHT_CHANNEL_WIDTH_160MHZ;
} else if (bcn->OperatingMode.chanWidth >=
eHT_CHANNEL_WIDTH_80MHZ) {
pe_debug("Updating the CH Width to 80MHz");
sta_ds->vhtSupportedChannelWidthSet =
WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ;
sta_ds->htSupportedChannelWidthSet =
eHT_CHANNEL_WIDTH_40MHZ;
ch_width = eHT_CHANNEL_WIDTH_80MHZ;
} else if (bcn->OperatingMode.chanWidth ==
eHT_CHANNEL_WIDTH_40MHZ) {
pe_debug("Updating the CH Width to 40MHz");
sta_ds->vhtSupportedChannelWidthSet =
WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ;
sta_ds->htSupportedChannelWidthSet =
eHT_CHANNEL_WIDTH_40MHZ;
ch_width = eHT_CHANNEL_WIDTH_40MHZ;
} else if (bcn->OperatingMode.chanWidth ==
eHT_CHANNEL_WIDTH_20MHZ) {
pe_debug("Updating the CH Width to 20MHz");
sta_ds->vhtSupportedChannelWidthSet =
WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ;
sta_ds->htSupportedChannelWidthSet =
eHT_CHANNEL_WIDTH_20MHZ;
ch_width = eHT_CHANNEL_WIDTH_20MHZ;
}
lim_check_vht_op_mode_change(mac_ctx, session,
ch_width, sta_ds->staIndex, mac_hdr->sa);
update_nss(mac_ctx, sta_ds, bcn, session, mac_hdr);
}
return;
}
if (!(session->vhtCapability && bcn->VHTOperation.present))
return;
oper_mode = sta_ds->vhtSupportedChannelWidthSet;
if ((oper_mode == WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) &&
(oper_mode < bcn->VHTOperation.chanWidth))
skip_opmode_update = true;
if (WNI_CFG_CHANNEL_BONDING_MODE_DISABLE == cb_mode) {
/*
* if channel bonding is disabled from INI and
* receiving beacon which has operating mode IE
* containing channel width change then don't update
* CH_WIDTH
*/
pe_err("CB disabled & CH_WIDTH changed old[%d] new[%d]",
oper_mode, bcn->OperatingMode.chanWidth);
return;
}
if (!skip_opmode_update &&
(oper_mode != bcn->VHTOperation.chanWidth)) {
pe_debug("received VHTOP CHWidth %d staIdx = %d",
bcn->VHTOperation.chanWidth, sta_ds->staIndex);
pe_debug("MAC - %0x:%0x:%0x:%0x:%0x:%0x",
mac_hdr->sa[0], mac_hdr->sa[1],
mac_hdr->sa[2], mac_hdr->sa[3],
mac_hdr->sa[4], mac_hdr->sa[5]);
if ((bcn->VHTOperation.chanWidth >=
WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) &&
(fw_vht_ch_wd > eHT_CHANNEL_WIDTH_80MHZ)) {
pe_debug("Updating the CH Width to 160MHz");
sta_ds->vhtSupportedChannelWidthSet =
bcn->VHTOperation.chanWidth;
sta_ds->htSupportedChannelWidthSet =
eHT_CHANNEL_WIDTH_40MHZ;
ch_width = eHT_CHANNEL_WIDTH_160MHZ;
} else if (bcn->VHTOperation.chanWidth >=
WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) {
pe_debug("Updating the CH Width to 80MHz");
sta_ds->vhtSupportedChannelWidthSet =
WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ;
sta_ds->htSupportedChannelWidthSet =
eHT_CHANNEL_WIDTH_40MHZ;
ch_width = eHT_CHANNEL_WIDTH_80MHZ;
} else if (bcn->VHTOperation.chanWidth ==
WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ) {
sta_ds->vhtSupportedChannelWidthSet =
WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ;
if (bcn->HTCaps.supportedChannelWidthSet) {
pe_debug("Updating the CH Width to 40MHz");
sta_ds->htSupportedChannelWidthSet =
eHT_CHANNEL_WIDTH_40MHZ;
ch_width = eHT_CHANNEL_WIDTH_40MHZ;
} else {
pe_debug("Updating the CH Width to 20MHz");
sta_ds->htSupportedChannelWidthSet =
eHT_CHANNEL_WIDTH_20MHZ;
ch_width = eHT_CHANNEL_WIDTH_20MHZ;
}
}
lim_check_vht_op_mode_change(mac_ctx, session, ch_width,
sta_ds->staIndex, mac_hdr->sa);
}
}
/*
* sch_bcn_process_sta_ibss() - Process the received beacon frame
* for sta, bt_amp_sta and ibss
*
* @mac_ctx: mac_ctx
* @bcn: beacon struct
* @rx_pkt_info: received packet info
* @session: pe session pointer
* @bssIdx: bss index
* @beaconParams: update beacon params
* @sendProbeReq: out flag to indicate if probe rsp is to be sent
* @pMh: mac header
*
* Process the received beacon frame for sta and ibss
*
* Return: void
*/
static void
sch_bcn_process_sta_ibss(tpAniSirGlobal mac_ctx,
tpSchBeaconStruct bcn,
uint8_t *rx_pkt_info,
tpPESession session, uint8_t *bssIdx,
tUpdateBeaconParams *beaconParams,
uint8_t *sendProbeReq, tpSirMacMgmtHdr pMh)
{
tpDphHashNode pStaDs = NULL;
uint16_t aid;
uint8_t cb_mode;
if (CHAN_ENUM_14 >= session->currentOperChannel) {
if (session->force_24ghz_in_ht20)
cb_mode = WNI_CFG_CHANNEL_BONDING_MODE_DISABLE;
else
cb_mode =
mac_ctx->roam.configParam.channelBondingMode24GHz;
} else
cb_mode = mac_ctx->roam.configParam.channelBondingMode5GHz;
/* check for VHT capability */
pStaDs = dph_lookup_hash_entry(mac_ctx, pMh->sa, &aid,
&session->dph.dphHashTable);
if ((NULL == pStaDs) || ((NULL != pStaDs) &&
(STA_INVALID_IDX == pStaDs->staIndex)))
return;
sch_bcn_update_opmode_change(mac_ctx, pStaDs, session, bcn, pMh,
cb_mode);
sch_bcn_update_he_ies(mac_ctx, pStaDs, session, bcn, pMh);
return;
}
/**
* get_local_power_constraint_beacon() - extracts local constraint
* from beacon
* @bcn: beacon structure
* @local_constraint: local constraint pointer
*
* Return: None
*/
#ifdef FEATURE_WLAN_ESE
static void get_local_power_constraint_beacon(
tpSchBeaconStruct bcn,
int8_t *local_constraint)
{
if (bcn->eseTxPwr.present)
*local_constraint = bcn->eseTxPwr.power_limit;
}
#else
static void get_local_power_constraint_beacon(
tpSchBeaconStruct bcn,
int8_t *local_constraint)
{
}
#endif
/*
* __sch_beacon_process_for_session() - Process the received beacon frame when
* station is not scanning and corresponding session is found
*
*
* @mac_ctx: mac_ctx
* @bcn: beacon struct
* @rx_pkt_info: received packet info
* @session: pe session pointer
*
* Following scenarios exist when Session exists
* IBSS STA receving beacons from IBSS Peers, who are part of IBSS.
* - call lim_handle_ibs_scoalescing with that session context.
* Infra STA receving beacons from AP to which it is connected
* - call sch_beacon_processFromAP with that session's context.
* - call sch_beacon_processFromAP with that session's context.
* (here need to make sure BTAP creates session entry for BT STA)
* - just update the beacon count for heart beat purposes for now,
* for now, don't process the beacon.
* Infra/IBSS both active and receives IBSS beacon:
* - call lim_handle_ibs_scoalescing with that session context.
* Infra/IBSS both active and receives Infra beacon:
* - call sch_beacon_processFromAP with that session's context.
* any updates to EDCA parameters will be effective for IBSS as well,
* even though no WMM for IBSS ?? Need to figure out how to handle
* this scenario.
* Infra/BTSTA both active and receive Infra beacon.
* - change in EDCA parameters on Infra affect the BTSTA link.
* Update the same parameters on BT link
* Infra/BTSTA both active and receive BT-AP beacon.
* - update beacon cnt for heartbeat
* Infra/BTAP both active and receive Infra beacon.
* - BT-AP starts advertising BE parameters from Infra AP, if they get
* changed.
* Infra/BTAP both active and receive BTSTA beacon.
* - update beacon cnt for heartbeat
*
* Return: void
*/
static void __sch_beacon_process_for_session(tpAniSirGlobal mac_ctx,
tpSchBeaconStruct bcn,
uint8_t *rx_pkt_info,
tpPESession session)
{
uint8_t bssIdx = 0;
tUpdateBeaconParams beaconParams;
uint8_t sendProbeReq = false;
tpSirMacMgmtHdr pMh = WMA_GET_RX_MAC_HEADER(rx_pkt_info);
int8_t regMax = 0, maxTxPower = 0, local_constraint;
qdf_mem_zero(&beaconParams, sizeof(tUpdateBeaconParams));
beaconParams.paramChangeBitmap = 0;
if (LIM_IS_IBSS_ROLE(session)) {
lim_handle_ibss_coalescing(mac_ctx, bcn, rx_pkt_info, session);
} else if (LIM_IS_STA_ROLE(session)) {
if (false == sch_bcn_process_sta(mac_ctx, bcn,
rx_pkt_info, session, &bssIdx,
&beaconParams, &sendProbeReq, pMh))
return;
}
/*
* For vht session, if opermode ie or vht oper IE is present
* bandwidth change will be taken care using these vht IEs.
*/
if (!(session->vhtCapability && (bcn->OperatingMode.present ||
bcn->VHTOperation.present)) && session->htCapability &&
bcn->HTInfo.present && !LIM_IS_IBSS_ROLE(session))
lim_update_sta_run_time_ht_switch_chnl_params(mac_ctx,
&bcn->HTInfo, bssIdx, session);
if ((LIM_IS_STA_ROLE(session) && !wma_is_csa_offload_enabled())
|| LIM_IS_IBSS_ROLE(session)) {
/* Channel Switch information element updated */
if (bcn->channelSwitchPresent) {
/*
* on receiving channel switch announcement from AP,
* delete all TDLS peers before leaving BSS and proceed
* for channel switch
*/
if (LIM_IS_STA_ROLE(session))
lim_delete_tdls_peers(mac_ctx, session);
lim_update_channel_switch(mac_ctx, bcn, session);
} else if (session->gLimSpecMgmt.dot11hChanSwState ==
eLIM_11H_CHANSW_RUNNING) {
lim_cancel_dot11h_channel_switch(mac_ctx, session);
}
}
if (LIM_IS_STA_ROLE(session)
|| LIM_IS_IBSS_ROLE(session))
sch_bcn_process_sta_ibss(mac_ctx, bcn,
rx_pkt_info, session, &bssIdx,
&beaconParams, &sendProbeReq, pMh);
/* Obtain the Max Tx power for the current regulatory */
regMax = cfg_get_regulatory_max_transmit_power(mac_ctx,
session->currentOperChannel);
local_constraint = regMax;
if (mac_ctx->roam.configParam.allow_tpc_from_ap) {
get_local_power_constraint_beacon(bcn, &local_constraint);
pe_debug("ESE localPowerConstraint = %d,",
local_constraint);
if (mac_ctx->rrm.rrmPEContext.rrmEnable &&
bcn->powerConstraintPresent) {
local_constraint = regMax;
local_constraint -=
bcn->localPowerConstraint.localPowerConstraints;
pe_debug("localPowerConstraint = %d,",
local_constraint);
}
}
maxTxPower = lim_get_max_tx_power(regMax, local_constraint,
mac_ctx->roam.configParam.nTxPowerCap);
pe_debug("RegMax = %d, MaxTx pwr = %d",
regMax, maxTxPower);
/* If maxTxPower is increased or decreased */
if (maxTxPower != session->maxTxPower) {
pe_debug(
FL("Local power constraint change, Updating new maxTx power %d from old pwr %d"),
maxTxPower, session->maxTxPower);
if (lim_send_set_max_tx_power_req(mac_ctx, maxTxPower, session)
== eSIR_SUCCESS)
session->maxTxPower = maxTxPower;
}
/* Indicate to LIM that Beacon is received */
if (bcn->HTInfo.present)
lim_received_hb_handler(mac_ctx,
(uint8_t) bcn->HTInfo.primaryChannel, session);
else
lim_received_hb_handler(mac_ctx, (uint8_t) bcn->channelNumber,
session);
/*
* I don't know if any additional IE is required here. Currently, not
* include addIE.
*/
if (sendProbeReq)
lim_send_probe_req_mgmt_frame(mac_ctx, &session->ssId,
session->bssId, session->currentOperChannel,
session->selfMacAddr, session->dot11mode, 0, NULL);
if ((false == mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running)
&& beaconParams.paramChangeBitmap) {
pe_warn("Beacon for session[%d] got changed.",
session->peSessionId);
pe_warn("sending beacon param change bitmap: 0x%x",
beaconParams.paramChangeBitmap);
lim_send_beacon_params(mac_ctx, &beaconParams, session);
}
if ((session->pePersona == QDF_P2P_CLIENT_MODE) &&
session->send_p2p_conf_frame) {
lim_p2p_oper_chan_change_confirm_action_frame(mac_ctx,
session->bssId, session);
session->send_p2p_conf_frame = false;
}
}
#ifdef WLAN_FEATURE_11AX_BSS_COLOR
static void ap_update_bss_color_info(tpAniSirGlobal mac_ctx,
tpPESession session,
uint8_t bss_color)
{
if (!session)
return;
if (bss_color < 1 || bss_color > 63) {
pe_warn("Invalid BSS color");
return;
}
session->bss_color_info[bss_color - 1].seen_count++;
session->bss_color_info[bss_color - 1].timestamp =
qdf_get_system_timestamp();
}
static uint8_t ap_get_new_bss_color(tpAniSirGlobal mac_ctx, tpPESession session)
{
int i;
uint8_t new_bss_color;
struct bss_color_info color_info;
qdf_time_t cur_timestamp;
if (!session)
return 0;
color_info = session->bss_color_info[0];
new_bss_color = 0;
cur_timestamp = qdf_get_system_timestamp();
for (i = 1; i < MAX_BSS_COLOR_VALUE; i++) {
if (session->bss_color_info[i].seen_count == 0) {
new_bss_color = i + 1;
return new_bss_color;
}
if (color_info.seen_count >
session->bss_color_info[i].seen_count &&
(cur_timestamp - session->bss_color_info[i].
timestamp) > TIME_BEACON_NOT_UPDATED) {
color_info = session->bss_color_info[i];
new_bss_color = i + 1;
}
}
pe_debug("new bss color: %d", new_bss_color);
return new_bss_color;
}
static void sch_check_bss_color_ie(tpAniSirGlobal mac_ctx,
tpPESession ap_session,
tSchBeaconStruct *bcn,
tUpdateBeaconParams *bcn_prm)
{
/* check bss color in the beacon */
if (ap_session->he_op.present && !ap_session->he_op.bss_color) {
if (bcn->he_op.present &&
(bcn->he_op.bss_color ==
ap_session->he_op.bss_color)) {
ap_session->he_op.bss_col_disabled = 1;
bcn_prm->paramChangeBitmap |=
PARAM_BSS_COLOR_CHANGED;
ap_session->he_bss_color_change.countdown =
BSS_COLOR_SWITCH_COUNTDOWN;
ap_session->he_bss_color_change.new_color =
ap_get_new_bss_color(mac_ctx,
ap_session);
ap_session->he_op.bss_color = ap_session->
he_bss_color_change.new_color;
bcn_prm->bss_color = ap_session->he_op.bss_color;
bcn_prm->bss_color_disabled =
ap_session->he_op.bss_col_disabled;
ap_session->bss_color_changing = 1;
} else {
/* update info for the bss color */
if (bcn->he_op.present)
ap_update_bss_color_info(mac_ctx,
ap_session,
bcn->he_op.bss_color);
}
}
}
#else
static void sch_check_bss_color_ie(tpAniSirGlobal mac_ctx,
tpPESession ap_session,
tSchBeaconStruct *bcn,
tUpdateBeaconParams *bcn_prm)
{
}
#endif
/**
* sch_beacon_process() - process the beacon frame
* @mac_ctx: mac global context
* @rx_pkt_info: pointer to buffer descriptor
*
* Return: None
*/
void
sch_beacon_process(tpAniSirGlobal mac_ctx, uint8_t *rx_pkt_info,
tpPESession session)
{
static tSchBeaconStruct bcn;
tUpdateBeaconParams bcn_prm;
tpPESession ap_session = NULL;
uint8_t i;
qdf_mem_zero(&bcn_prm, sizeof(tUpdateBeaconParams));
bcn_prm.paramChangeBitmap = 0;
/* Convert the beacon frame into a structure */
if (sir_convert_beacon_frame2_struct(mac_ctx, (uint8_t *) rx_pkt_info,
&bcn) != eSIR_SUCCESS) {
pe_err("beacon parsing failed");
return;
}
if (bcn.ssidPresent)
bcn.ssId.ssId[bcn.ssId.length] = 0;
/*
* First process the beacon in the context of any existing AP or BTAP
* session. This takes cares of following two scenarios:
* - session = NULL:
* e.g. beacon received from a neighboring BSS, you want to apply the
* protection settings to BTAP/InfraAP beacons
* - session is non NULL:
* e.g. beacon received is from the INFRA AP to which you are connected
* on another concurrent link. In this case also, we want to apply the
* protection settings(as advertised by Infra AP) to BTAP beacons
*/
for (i = 0; i < mac_ctx->lim.maxBssId; i++) {
ap_session = pe_find_session_by_session_id(mac_ctx, i);
if (!((ap_session != NULL) &&
(!(WMA_GET_OFFLOADSCANLEARN(rx_pkt_info)))))
continue;
if (!LIM_IS_AP_ROLE(ap_session))
continue;
bcn_prm.bssIdx = ap_session->bssIdx;
if (!ap_session->is_session_obss_color_collision_det_enabled)
sch_check_bss_color_ie(mac_ctx, ap_session,
&bcn, &bcn_prm);
if ((ap_session->gLimProtectionControl !=
WNI_CFG_FORCE_POLICY_PROTECTION_DISABLE) &&
!ap_session->is_session_obss_offload_enabled)
ap_beacon_process(mac_ctx, rx_pkt_info,
&bcn, &bcn_prm, ap_session);
if ((false == mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running)
&& bcn_prm.paramChangeBitmap) {
/* Update the bcn and apply the new settings to HAL */
sch_set_fixed_beacon_fields(mac_ctx, ap_session);
pe_debug("Beacon for PE session[%d] got changed",
ap_session->peSessionId);
pe_debug("sending beacon param change bitmap: 0x%x",
bcn_prm.paramChangeBitmap);
lim_send_beacon_params(mac_ctx, &bcn_prm, ap_session);
}
}
/*
* Now process the beacon in the context of the BSS which is
* transmitting the beacons, if one is found
*/
if (session == NULL)
__sch_beacon_process_no_session(mac_ctx, &bcn, rx_pkt_info);
else
__sch_beacon_process_for_session(mac_ctx, &bcn, rx_pkt_info,
session);
}
/**
* sch_beacon_edca_process(): Process the EDCA parameter set in the received
* beacon frame
*
* @mac_ctx: mac global context
* @edca: reference to edca parameters in beacon struct
* @session : pesession entry
*
* @return status of operation
*/
tSirRetStatus
sch_beacon_edca_process(tpAniSirGlobal pMac, tSirMacEdcaParamSetIE *edca,
tpPESession session)
{
uint8_t i;
#ifdef FEATURE_WLAN_DIAG_SUPPORT
host_log_qos_edca_pkt_type *log_ptr = NULL;
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
pe_debug("Updating parameter set count: Old %d ---> new %d",
session->gLimEdcaParamSetCount, edca->qosInfo.count);
session->gLimEdcaParamSetCount = edca->qosInfo.count;
session->gLimEdcaParams[EDCA_AC_BE] = edca->acbe;
session->gLimEdcaParams[EDCA_AC_BK] = edca->acbk;
session->gLimEdcaParams[EDCA_AC_VI] = edca->acvi;
session->gLimEdcaParams[EDCA_AC_VO] = edca->acvo;
if (pMac->roam.configParam.enable_edca_params) {
session->gLimEdcaParams[EDCA_AC_VO].aci.aifsn =
pMac->roam.configParam.edca_vo_aifs;
session->gLimEdcaParams[EDCA_AC_VI].aci.aifsn =
pMac->roam.configParam.edca_vi_aifs;
session->gLimEdcaParams[EDCA_AC_BK].aci.aifsn =
pMac->roam.configParam.edca_bk_aifs;
session->gLimEdcaParams[EDCA_AC_BE].aci.aifsn =
pMac->roam.configParam.edca_be_aifs;
session->gLimEdcaParams[EDCA_AC_VO].cw.min =
pMac->roam.configParam.edca_vo_cwmin;
session->gLimEdcaParams[EDCA_AC_VI].cw.min =
pMac->roam.configParam.edca_vi_cwmin;
session->gLimEdcaParams[EDCA_AC_BK].cw.min =
pMac->roam.configParam.edca_bk_cwmin;
session->gLimEdcaParams[EDCA_AC_BE].cw.min =
pMac->roam.configParam.edca_be_cwmin;
session->gLimEdcaParams[EDCA_AC_VO].cw.max =
pMac->roam.configParam.edca_vo_cwmax;
session->gLimEdcaParams[EDCA_AC_VI].cw.max =
pMac->roam.configParam.edca_vi_cwmax;
session->gLimEdcaParams[EDCA_AC_BK].cw.max =
pMac->roam.configParam.edca_bk_cwmax;
session->gLimEdcaParams[EDCA_AC_BE].cw.max =
pMac->roam.configParam.edca_be_cwmax;
}
#ifdef FEATURE_WLAN_DIAG_SUPPORT
WLAN_HOST_DIAG_LOG_ALLOC(log_ptr, host_log_qos_edca_pkt_type,
LOG_WLAN_QOS_EDCA_C);
if (log_ptr) {
log_ptr->aci_be = session->gLimEdcaParams[EDCA_AC_BE].aci.aci;
log_ptr->cw_be =
session->gLimEdcaParams[EDCA_AC_BE].cw.max << 4
| session->gLimEdcaParams[EDCA_AC_BE].cw.min;
log_ptr->txoplimit_be =
session->gLimEdcaParams[EDCA_AC_BE].txoplimit;
log_ptr->aci_bk =
session->gLimEdcaParams[EDCA_AC_BK].aci.aci;
log_ptr->cw_bk =
session->gLimEdcaParams[EDCA_AC_BK].cw.max << 4
| session->gLimEdcaParams[EDCA_AC_BK].cw.min;
log_ptr->txoplimit_bk =
session->gLimEdcaParams[EDCA_AC_BK].txoplimit;
log_ptr->aci_vi =
session->gLimEdcaParams[EDCA_AC_VI].aci.aci;
log_ptr->cw_vi =
session->gLimEdcaParams[EDCA_AC_VI].cw.max << 4
| session->gLimEdcaParams[EDCA_AC_VI].cw.min;
log_ptr->txoplimit_vi =
session->gLimEdcaParams[EDCA_AC_VI].txoplimit;
log_ptr->aci_vo =
session->gLimEdcaParams[EDCA_AC_VO].aci.aci;
log_ptr->cw_vo =
session->gLimEdcaParams[EDCA_AC_VO].cw.max << 4
| session->gLimEdcaParams[EDCA_AC_VO].cw.min;
log_ptr->txoplimit_vo =
session->gLimEdcaParams[EDCA_AC_VO].txoplimit;
}
WLAN_HOST_DIAG_LOG_REPORT(log_ptr);
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
pe_debug("Edsa param enabled in ini %d. Updating Local EDCA Params(gLimEdcaParams) to: ",
pMac->roam.configParam.enable_edca_params);
for (i = 0; i < MAX_NUM_AC; i++) {
pe_debug("AC[%d]: AIFSN: %d, ACM %d, CWmin %d, CWmax %d, TxOp %d",
i, session->gLimEdcaParams[i].aci.aifsn,
session->gLimEdcaParams[i].aci.acm,
session->gLimEdcaParams[i].cw.min,
session->gLimEdcaParams[i].cw.max,
session->gLimEdcaParams[i].txoplimit);
}
return eSIR_SUCCESS;
}
void lim_enable_obss_detection_config(tpAniSirGlobal mac_ctx,
tpPESession session)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (!session) {
pe_err("Invalid session, protection not enabled");
return;
}
if (session->gLimProtectionControl ==
WNI_CFG_FORCE_POLICY_PROTECTION_DISABLE) {
pe_err("protectiond disabled, force policy, session %d",
session->smeSessionId);
return;
}
if (mac_ctx->lim.global_obss_offload_enabled) {
status = lim_obss_send_detection_cfg(mac_ctx, session, true);
if (QDF_IS_STATUS_ERROR(status)) {
pe_err("vdev %d: offload enable failed, trying legacy",
session->smeSessionId);
session->is_session_obss_offload_enabled = false;
} else {
pe_debug("vdev %d: offload detection enabled",
session->smeSessionId);
session->is_session_obss_offload_enabled = true;
lim_obss_send_detection_cfg(mac_ctx, session, true);
}
}
if (!mac_ctx->lim.global_obss_offload_enabled ||
QDF_IS_STATUS_ERROR(status)) {
status = qdf_mc_timer_start(&session->
protection_fields_reset_timer,
SCH_PROTECTION_RESET_TIME);
if (QDF_IS_STATUS_ERROR(status))
pe_err("vdev %d: start timer failed",
session->smeSessionId);
else
pe_debug("vdev %d: legacy detection enabled",
session->smeSessionId);
}
}
QDF_STATUS lim_obss_generate_detection_config(tpAniSirGlobal mac_ctx,
tpPESession session,
struct obss_detection_cfg *cfg)
{
uint32_t phy_mode;
enum band_info rf_band = BAND_UNKNOWN;
struct obss_detection_cfg *cur_detect;
if (!mac_ctx || !session || !cfg) {
pe_err("Invalid params mac_ctx %pK, session %pK, cfg %pK",
mac_ctx, session, cfg);
return QDF_STATUS_E_INVAL;
}
lim_get_phy_mode(mac_ctx, &phy_mode, session);
rf_band = session->limRFBand;
qdf_mem_zero(cfg, sizeof(*cfg));
cur_detect = &session->current_obss_detection;
pe_debug("band:%d, phy_mode:%d, ht_cap:%d, ht_oper_mode:%d",
rf_band, phy_mode, session->htCapability,
mac_ctx->lim.gHTOperMode);
pe_debug("assoc_sta: 11b:%d, 11g:%d, 11a:%d, ht20:%d",
session->gLim11bParams.protectionEnabled,
session->gLim11gParams.protectionEnabled,
session->gLim11aParams.protectionEnabled,
session->gLimHt20Params.protectionEnabled);
pe_debug("obss: 11b:%d, 11g:%d, 11a:%d, ht20:%d",
session->gLimOlbcParams.protectionEnabled,
session->gLimOverlap11gParams.protectionEnabled,
session->gLimOverlap11aParams.protectionEnabled,
session->gLimOverlapHt20Params.protectionEnabled);
pe_debug("detect: b_ap:%d, b_s:%d, g:%d, a:%d, htl:%d, htm:%d, ht20:%d",
cur_detect->obss_11b_ap_detect_mode,
cur_detect->obss_11b_sta_detect_mode,
cur_detect->obss_11g_ap_detect_mode,
cur_detect->obss_11a_detect_mode,
cur_detect->obss_ht_legacy_detect_mode,
cur_detect->obss_ht_mixed_detect_mode,
cur_detect->obss_ht_20mhz_detect_mode);
if ((rf_band == BAND_2G)) {
if ((phy_mode == WNI_CFG_PHY_MODE_11G ||
session->htCapability) &&
!session->gLim11bParams.protectionEnabled) {
if (!session->gLimOlbcParams.protectionEnabled &&
!session->gLimOverlap11gParams.protectionEnabled) {
cfg->obss_11b_ap_detect_mode =
OBSS_OFFLOAD_DETECTION_PRESENT;
cfg->obss_11b_sta_detect_mode =
OBSS_OFFLOAD_DETECTION_PRESENT;
} else {
if (cur_detect->obss_11b_ap_detect_mode ==
OBSS_OFFLOAD_DETECTION_PRESENT)
cfg->obss_11b_ap_detect_mode =
OBSS_OFFLOAD_DETECTION_ABSENT;
if (cur_detect->obss_11b_sta_detect_mode ==
OBSS_OFFLOAD_DETECTION_PRESENT)
cfg->obss_11b_sta_detect_mode =
OBSS_OFFLOAD_DETECTION_ABSENT;
}
} else if (session->gLim11bParams.protectionEnabled) {
session->gLimOlbcParams.protectionEnabled = false;
}
if (session->htCapability &&
session->cfgProtection.overlapFromllg &&
!session->gLim11gParams.protectionEnabled) {
if (!session->gLimOverlap11gParams.protectionEnabled) {
cfg->obss_11g_ap_detect_mode =
OBSS_OFFLOAD_DETECTION_PRESENT;
cfg->obss_ht_legacy_detect_mode =
OBSS_OFFLOAD_DETECTION_PRESENT;
cfg->obss_ht_mixed_detect_mode =
OBSS_OFFLOAD_DETECTION_PRESENT;
} else {
if (cur_detect->obss_11g_ap_detect_mode ==
OBSS_OFFLOAD_DETECTION_PRESENT)
cfg->obss_11g_ap_detect_mode =
OBSS_OFFLOAD_DETECTION_ABSENT;
if (cur_detect->obss_ht_legacy_detect_mode ==
OBSS_OFFLOAD_DETECTION_PRESENT)
cfg->obss_ht_legacy_detect_mode =
OBSS_OFFLOAD_DETECTION_ABSENT;
if (cur_detect->obss_ht_mixed_detect_mode ==
OBSS_OFFLOAD_DETECTION_PRESENT)
cfg->obss_ht_mixed_detect_mode =
OBSS_OFFLOAD_DETECTION_ABSENT;
}
} else if (session->gLim11gParams.protectionEnabled) {
session->gLimOverlap11gParams.protectionEnabled = false;
}
/* INI related settings */
if (mac_ctx->roam.configParam.ignore_peer_erp_info)
cfg->obss_11b_sta_detect_mode =
OBSS_OFFLOAD_DETECTION_DISABLED;
if (mac_ctx->roam.configParam.ignore_peer_ht_opmode)
cfg->obss_ht_legacy_detect_mode =
OBSS_OFFLOAD_DETECTION_DISABLED;
}
if ((rf_band == BAND_5G) && session->htCapability) {
if (!session->gLim11aParams.protectionEnabled) {
if (!session->gLimOverlap11aParams.protectionEnabled)
cfg->obss_11a_detect_mode =
OBSS_OFFLOAD_DETECTION_PRESENT;
else if (cur_detect->obss_11a_detect_mode ==
OBSS_OFFLOAD_DETECTION_PRESENT)
cfg->obss_11a_detect_mode =
OBSS_OFFLOAD_DETECTION_ABSENT;
} else {
session->gLimOverlap11aParams.protectionEnabled = false;
}
}
if (((rf_band == BAND_2G) || (rf_band == BAND_5G)) &&
session->htCapability) {
if (!session->gLimHt20Params.protectionEnabled) {
if (!session->gLimOverlapHt20Params.protectionEnabled) {
cfg->obss_ht_20mhz_detect_mode =
OBSS_OFFLOAD_DETECTION_PRESENT;
} else if (cur_detect->obss_ht_20mhz_detect_mode ==
OBSS_OFFLOAD_DETECTION_PRESENT) {
cfg->obss_ht_20mhz_detect_mode =
OBSS_OFFLOAD_DETECTION_ABSENT;
}
} else {
session->gLimOverlapHt20Params.protectionEnabled =
false;
}
}
pe_debug("b_ap:%d, b_s:%d, g:%d, a:%d, ht_le:%d, ht_m:%d, ht_20:%d",
cfg->obss_11b_ap_detect_mode,
cfg->obss_11b_sta_detect_mode,
cfg->obss_11g_ap_detect_mode,
cfg->obss_11a_detect_mode,
cfg->obss_ht_legacy_detect_mode,
cfg->obss_ht_mixed_detect_mode,
cfg->obss_ht_20mhz_detect_mode);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS lim_obss_send_detection_cfg(tpAniSirGlobal mac_ctx,
tpPESession session, bool force)
{
QDF_STATUS status;
struct obss_detection_cfg obss_cfg;
struct wmi_obss_detection_cfg_param *req_param;
if (!session) {
pe_err("Invalid session");
return QDF_STATUS_E_INVAL;
}
if (!session->is_session_obss_offload_enabled) {
pe_debug("obss offload protectiond disabled, session %d",
session->smeSessionId);
/* Send success */
return QDF_STATUS_SUCCESS;
}
if (session->gLimProtectionControl ==
WNI_CFG_FORCE_POLICY_PROTECTION_DISABLE) {
pe_debug("protectiond disabled, force from policy, session %d",
session->smeSessionId);
/* Send success */
return QDF_STATUS_SUCCESS;
}
status = lim_obss_generate_detection_config(mac_ctx,
session,
&obss_cfg);
if (QDF_IS_STATUS_ERROR(status)) {
pe_err("Failed to generate obss detection cfg, session %d",
session->smeSessionId);
return status;
}
if (qdf_mem_cmp(&session->obss_offload_cfg, &obss_cfg, sizeof(obss_cfg))
|| force) {
struct scheduler_msg msg = {0};
req_param = qdf_mem_malloc(sizeof(*req_param));
if (!req_param) {
pe_err("Failed to allocate memory");
return QDF_STATUS_E_NOMEM;
}
qdf_mem_copy(&session->obss_offload_cfg, &obss_cfg,
sizeof(obss_cfg));
req_param->vdev_id = session->smeSessionId;
req_param->obss_detect_period_ms = OBSS_DETECTION_PERIOD_MS;
req_param->obss_11b_ap_detect_mode =
obss_cfg.obss_11b_ap_detect_mode;
req_param->obss_11b_sta_detect_mode =
obss_cfg.obss_11b_sta_detect_mode;
req_param->obss_11g_ap_detect_mode =
obss_cfg.obss_11g_ap_detect_mode;
req_param->obss_11a_detect_mode =
obss_cfg.obss_11a_detect_mode;
req_param->obss_ht_legacy_detect_mode =
obss_cfg.obss_ht_legacy_detect_mode;
req_param->obss_ht_20mhz_detect_mode =
obss_cfg.obss_ht_20mhz_detect_mode;
req_param->obss_ht_mixed_detect_mode =
obss_cfg.obss_ht_mixed_detect_mode;
msg.type = WMA_OBSS_DETECTION_REQ;
msg.bodyptr = req_param;
msg.reserved = 0;
status = scheduler_post_msg(QDF_MODULE_ID_WMA, &msg);
if (QDF_IS_STATUS_ERROR(status)) {
pe_err("Failed to post WMA_OBSS_DETECTION_REQ to WMA");
qdf_mem_free(req_param);
return status;
}
} else {
pe_debug("Skiping WMA_OBSS_DETECTION_REQ, force = %d", force);
}
return status;
}
QDF_STATUS lim_process_obss_detection_ind(tpAniSirGlobal mac_ctx,
struct wmi_obss_detect_info
*obss_detection)
{
QDF_STATUS status;
uint32_t detect_masks;
uint32_t reason;
struct obss_detection_cfg *obss_cfg;
bool enable;
tpPESession session;
tUpdateBeaconParams bcn_prm;
enum band_info rf_band = BAND_UNKNOWN;
struct obss_detection_cfg *cur_detect;
pe_debug("obss detect ind id %d, reason %d, msk 0x%x, " MAC_ADDRESS_STR,
obss_detection->vdev_id, obss_detection->reason,
obss_detection->matched_detection_masks,
MAC_ADDR_ARRAY(obss_detection->matched_bssid_addr));
session = pe_find_session_by_sme_session_id(mac_ctx,
obss_detection->vdev_id);
if (!session) {
pe_err("Failed to get session for id %d",
obss_detection->vdev_id);
return QDF_STATUS_E_INVAL;
}
if (!LIM_IS_AP_ROLE(session)) {
pe_err("session %d is not AP", obss_detection->vdev_id);
return QDF_STATUS_E_INVAL;
}
if (!session->is_session_obss_offload_enabled) {
pe_err("Offload already disabled for session %d",
obss_detection->vdev_id);
return QDF_STATUS_SUCCESS;
}
reason = obss_detection->reason;
detect_masks = obss_detection->matched_detection_masks;
if (reason == OBSS_OFFLOAD_DETECTION_PRESENT) {
enable = true;
} else if (reason == OBSS_OFFLOAD_DETECTION_ABSENT) {
enable = false;
} else if (reason == OBSS_OFFLOAD_DETECTION_DISABLED) {
/*
* Most common reason for this event-type from firmware
* is insufficient memory.
* Disable offload OBSS detection and enable legacy-way
* of detecting OBSS by parsing beacons.
**/
session->is_session_obss_offload_enabled = false;
pe_err("FW indicated obss offload disabled");
pe_err("Enabling host based detection, session %d",
obss_detection->vdev_id);
status = qdf_mc_timer_start(&session->
protection_fields_reset_timer,
SCH_PROTECTION_RESET_TIME);
if (QDF_IS_STATUS_ERROR(status))
pe_err("cannot start protection reset timer");
return QDF_STATUS_SUCCESS;
} else {
pe_err("Invalid reason %d, session %d",
obss_detection->reason,
obss_detection->vdev_id);
return QDF_STATUS_E_INVAL;
}
rf_band = session->limRFBand;
qdf_mem_zero(&bcn_prm, sizeof(bcn_prm));
obss_cfg = &session->obss_offload_cfg;
cur_detect = &session->current_obss_detection;
if (OBSS_DETECTION_IS_11B_AP(detect_masks)) {
if (reason != obss_cfg->obss_11b_ap_detect_mode ||
rf_band != BAND_2G)
goto wrong_detection;
lim_enable11g_protection(mac_ctx, enable, true,
&bcn_prm, session);
cur_detect->obss_11b_ap_detect_mode = reason;
}
if (OBSS_DETECTION_IS_11B_STA(detect_masks)) {
if (reason != obss_cfg->obss_11b_sta_detect_mode ||
rf_band != BAND_2G)
goto wrong_detection;
lim_enable11g_protection(mac_ctx, enable, true,
&bcn_prm, session);
cur_detect->obss_11b_sta_detect_mode = reason;
}
if (OBSS_DETECTION_IS_11G_AP(detect_masks)) {
if (reason != obss_cfg->obss_11g_ap_detect_mode ||
rf_band != BAND_2G)
goto wrong_detection;
lim_enable_ht_protection_from11g(mac_ctx, enable, true,
&bcn_prm, session);
cur_detect->obss_11g_ap_detect_mode = reason;
}
if (OBSS_DETECTION_IS_11A(detect_masks)) {
if (reason != obss_cfg->obss_11a_detect_mode ||
rf_band != BAND_5G)
goto wrong_detection;
lim_update_11a_protection(mac_ctx, enable, true,
&bcn_prm, session);
cur_detect->obss_11a_detect_mode = reason;
}
if (OBSS_DETECTION_IS_HT_LEGACY(detect_masks)) {
/* for 5GHz, we have only 11a detection, which covers legacy */
if (reason != obss_cfg->obss_ht_legacy_detect_mode ||
rf_band != BAND_2G)
goto wrong_detection;
lim_enable_ht_protection_from11g(mac_ctx, enable, true,
&bcn_prm, session);
cur_detect->obss_ht_legacy_detect_mode = reason;
}
if (OBSS_DETECTION_IS_HT_MIXED(detect_masks)) {
/* for 5GHz, we have only 11a detection, which covers ht mix */
if (reason != obss_cfg->obss_ht_mixed_detect_mode ||
rf_band != BAND_2G)
goto wrong_detection;
lim_enable_ht_protection_from11g(mac_ctx, enable, true,
&bcn_prm, session);
cur_detect->obss_ht_mixed_detect_mode = reason;
}
if (OBSS_DETECTION_IS_HT_20MHZ(detect_masks)) {
if (reason != obss_cfg->obss_ht_20mhz_detect_mode)
goto wrong_detection;
lim_enable_ht20_protection(mac_ctx, enable, true,
&bcn_prm, session);
cur_detect->obss_ht_20mhz_detect_mode = reason;
}
if ((false == mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running) &&
bcn_prm.paramChangeBitmap) {
/* Update the bcn and apply the new settings to HAL */
sch_set_fixed_beacon_fields(mac_ctx, session);
pe_debug("Beacon for PE session: %d got changed: 0x%x",
session->smeSessionId, bcn_prm.paramChangeBitmap);
if (!IS_SIR_STATUS_SUCCESS(lim_send_beacon_params(
mac_ctx, &bcn_prm, session))) {
pe_err("Failed to send beacon param, session %d",
obss_detection->vdev_id);
return QDF_STATUS_E_FAULT;
}
}
status = lim_obss_send_detection_cfg(mac_ctx, session, true);
if (QDF_IS_STATUS_ERROR(status)) {
pe_err("Failed to send obss detection cfg, session %d",
obss_detection->vdev_id);
return status;
}
return QDF_STATUS_SUCCESS;
wrong_detection:
/*
* We may get this wrong detection before FW can update latest cfg,
* So keeping log level debug
**/
pe_debug("Wrong detection, session %d", obss_detection->vdev_id);
return QDF_STATUS_E_INVAL;
}