blob: abd22d6d9a248d35c8a4d2b6ef181514df0ed50f [file] [log] [blame]
/*
* Copyright (c) 2012-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.
*/
/*
* 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_ucfg_api.h"
#include "lim_api.h"
#include "utils_api.h"
#include "sch_api.h"
#include "lim_utils.h"
#include "lim_send_messages.h"
#include "rrm_api.h"
#ifdef FEATURE_WLAN_DIAG_SUPPORT
#include "host_diag_core_log.h"
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
#include "wma.h"
static void
ap_beacon_process_5_ghz(struct mac_context *mac_ctx, uint8_t *rx_pkt_info,
tpSchBeaconStruct bcn_struct,
tpUpdateBeaconParams bcn_prm, struct pe_session *session,
uint32_t phy_mode)
{
tpSirMacMgmtHdr mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info);
if (!session->htCapability)
return;
if (bcn_struct->channelNumber != wlan_reg_freq_to_chan(
mac_ctx->pdev, session->curr_op_freq))
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(struct mac_context *mac_ctx, uint8_t *rx_pkt_info,
tpSchBeaconStruct bcn_struct,
tpUpdateBeaconParams bcn_prm, struct pe_session *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 != wlan_reg_freq_to_chan(
mac_ctx->pdev, session->curr_op_freq))
return;
tmp_exp = (!bcn_struct->erpPresent &&
!bcn_struct->HTInfo.present) ||
/* if erp not present then 11B AP overlapping */
(!mac_ctx->mlme_cfg->sta.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 != wlan_reg_freq_to_chan(
mac_ctx->pdev, session->curr_op_freq))
return;
tmp_exp = (!bcn_struct->erpPresent && !bcn_struct->HTInfo.present) ||
/* if erp not present then 11B AP overlapping */
(!mac_ctx->mlme_cfg->sta.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->mlme_cfg->sap_protection_cfg.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(struct mac_context *mac_ctx, uint8_t *rx_pkt_info,
tpSchBeaconStruct bcn_struct,
tpUpdateBeaconParams bcn_prm, struct pe_session *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(struct mac_context *mac,
tpSchBeaconStruct pBeacon,
uint8_t *pRxPacketInfo)
{
struct pe_session *pe_session = NULL;
pe_session = lim_is_ibss_session_active(mac);
if (pe_session) {
lim_handle_ibss_coalescing(mac, pBeacon, pRxPacketInfo,
pe_session);
}
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
* @mac_ctx: mac_ctx
* @bcn: beacon struct
* @rx_pkt_info: received packet info
* @session: pe session pointer
* @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(struct mac_context *mac_ctx,
tpSchBeaconStruct bcn,
uint8_t *rx_pkt_info,
struct pe_session *session,
tUpdateBeaconParams *beaconParams,
uint8_t *sendProbeReq, tpSirMacMgmtHdr pMh)
{
uint32_t bi;
tpDphHashNode sta = NULL;
QDF_STATUS status;
/*
* This handles two cases:
* -- Infra STA receiving 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 (bcn->channelNumber != wlan_reg_freq_to_chan(
mac_ctx->pdev, session->curr_op_freq)) {
pe_err("Channel Change freq from %d --> %d - Ignoring beacon!",
session->curr_op_freq,
wlan_reg_chan_to_freq(
mac_ctx->pdev, bcn->channelNumber));
return false;
}
/*
* Ignore bcn as channel switch IE present and csa offload is enabled,
* as in CSA offload enabled case FW will send Event to switch channel
*/
if (bcn->channelSwitchPresent && wma_is_csa_offload_enabled()) {
pe_err_rl("Ignore bcn as channel switch IE present and csa offload is enabled");
return false;
}
lim_detect_change_in_ap_capabilities(mac_ctx, bcn, session);
beaconParams->bss_idx = session->vdev_id;
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) {
if (!cfg_in_range(CFG_CFP_PERIOD, bcn->cfParamSet.cfpPeriod)) {
pe_err("Error in setting CFG item CFP Period");
return false;
}
mac_ctx->mlme_cfg->rates.cfp_period = bcn->cfParamSet.cfpPeriod;
}
/* No need to send DTIM Period and Count to HAL/SMAC */
/* SMAC already parses TIM bit. */
if (bcn->timPresent) {
if (cfg_in_range(CFG_DTIM_PERIOD, bcn->tim.dtimPeriod))
mac_ctx->mlme_cfg->sap_cfg.dtim_interval =
bcn->tim.dtimPeriod;
}
if (mac_ctx->lim.gLimProtectionControl !=
MLME_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);
sta = 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) {
status = sch_beacon_edca_process(mac_ctx,
&bcn->edcaParams,
session);
if (QDF_IS_STATUS_ERROR(status)) {
pe_err("EDCA parameter processing error");
} else if (sta) {
/* If needed, downgrade the EDCA parameters */
lim_set_active_edca_params(mac_ctx,
session->gLimEdcaParams, session);
lim_send_edca_params(mac_ctx,
session->gLimEdcaParamsActive,
session->vdev_id, false);
} 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 struct pe_session *
* @mgmt_hdr: pointer to tpSirMacMgmtHdr
*
* function to update NSS
*
* Return: none
*/
static void update_nss(struct mac_context *mac_ctx, tpDphHashNode sta_ds,
tpSchBeaconStruct beacon, struct pe_session *session_entry,
tpSirMacMgmtHdr mgmt_hdr)
{
if (sta_ds->vhtSupportedRxNss != (beacon->OperatingMode.rxNSS + 1)) {
if (session_entry->nss_forced_1x1) {
pe_debug("Not Updating NSS for special AP");
return;
}
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(struct mac_context *mac_ctx, tpDphHashNode sta_ds,
struct pe_session *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(struct mac_context *mac_ctx, tpDphHashNode sta_ds,
struct pe_session *session, tpSchBeaconStruct bcn,
tpSirMacMgmtHdr mac_hdr)
{
return;
}
#endif
static void
sch_bcn_update_opmode_change(struct mac_context *mac_ctx, tpDphHashNode sta_ds,
struct pe_session *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;
/*
* Ignore opmode change during channel change The opmode will be updated
* with the beacons on new channel once the AP move to new channel.
*/
if (session->ch_switch_in_progress) {
pe_debug("Ignore opmode change as channel switch is in progress");
return;
}
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 do not
* update the chan width
*/
pe_debug_rl("CB disabled skip bw update: 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 do not
* update the chan width
*/
pe_debug_rl("CB disabled skip bw update: 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 and ibss
* @mac_ctx: mac_ctx
* @bcn: beacon struct
* @rx_pkt_info: received packet info
* @session: pe session pointer
* @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(struct mac_context *mac_ctx,
tpSchBeaconStruct bcn,
uint8_t *rx_pkt_info,
struct pe_session *session,
tUpdateBeaconParams *beaconParams,
uint8_t *sendProbeReq, tpSirMacMgmtHdr pMh)
{
tpDphHashNode sta = NULL;
uint16_t aid;
uint8_t cb_mode;
if (wlan_reg_is_24ghz_ch_freq(session->curr_op_freq)) {
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 */
sta = dph_lookup_hash_entry(mac_ctx, pMh->sa, &aid,
&session->dph.dphHashTable);
if ((!sta) || ((sta) &&
(STA_INVALID_IDX == sta->staIndex)))
return;
sch_bcn_update_opmode_change(mac_ctx, sta, session, bcn, pMh,
cb_mode);
sch_bcn_update_he_ies(mac_ctx, sta, 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 receiving beacons from IBSS Peers, who are part of IBSS.
* - call lim_handle_ibs_scoalescing with that session context.
* Infra STA receiving 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(struct mac_context *mac_ctx,
tpSchBeaconStruct bcn,
uint8_t *rx_pkt_info,
struct pe_session *session)
{
tUpdateBeaconParams beaconParams;
uint8_t sendProbeReq = false;
tpSirMacMgmtHdr pMh = WMA_GET_RX_MAC_HEADER(rx_pkt_info);
int8_t regMax = 0, maxTxPower = 0, local_constraint;
struct lim_max_tx_pwr_attr tx_pwr_attr = {0};
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, &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, 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_update_tdls_set_state_for_fw(session,
false);
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,
&beaconParams, &sendProbeReq, pMh);
/* Obtain the Max Tx power for the current regulatory */
regMax = lim_get_regulatory_max_transmit_power(
mac_ctx, wlan_reg_freq_to_chan(
mac_ctx->pdev, session->curr_op_freq));
local_constraint = regMax;
if (mac_ctx->mlme_cfg->sta.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);
}
}
tx_pwr_attr.reg_max = regMax;
tx_pwr_attr.ap_tx_power = local_constraint;
tx_pwr_attr.ini_tx_power = mac_ctx->mlme_cfg->power.max_tx_power;
tx_pwr_attr.frequency = session->curr_op_freq;
maxTxPower = lim_get_max_tx_power(mac_ctx, &tx_pwr_attr);
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)
== QDF_STATUS_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, wlan_reg_freq_to_chan(
mac_ctx->pdev, session->curr_op_freq),
session->self_mac_addr, session->dot11mode, NULL, NULL);
if ((false == mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running)
&& beaconParams.paramChangeBitmap) {
pe_debug("Beacon for session[%d] got changed.",
session->peSessionId);
pe_debug("sending beacon param change bitmap: 0x%x",
beaconParams.paramChangeBitmap);
lim_send_beacon_params(mac_ctx, &beaconParams, session);
}
if ((session->opmode == 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(struct mac_context *mac_ctx,
struct pe_session *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(struct mac_context *mac_ctx, struct pe_session *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(struct mac_context *mac_ctx,
struct pe_session *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(struct mac_context *mac_ctx,
struct pe_session *ap_session,
tSchBeaconStruct *bcn,
tUpdateBeaconParams *bcn_prm)
{
}
#endif
void sch_beacon_process_for_ap(struct mac_context *mac_ctx,
uint8_t session_id,
uint8_t *rx_pkt_info,
tSchBeaconStruct *bcn)
{
struct pe_session *ap_session;
tUpdateBeaconParams bcn_prm;
if (!bcn || !rx_pkt_info) {
pe_debug("bcn %pK or rx_pkt_info %pKis NULL",
bcn, rx_pkt_info);
return;
}
ap_session = pe_find_session_by_session_id(mac_ctx, session_id);
if (!ap_session)
return;
if (!LIM_IS_AP_ROLE(ap_session))
return;
qdf_mem_zero(&bcn_prm, sizeof(tUpdateBeaconParams));
bcn_prm.paramChangeBitmap = 0;
bcn_prm.bss_idx = ap_session->vdev_id;
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 !=
MLME_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);
}
}
#ifdef WLAN_BCN_RECV_FEATURE
/*
* sch_send_beacon_report() - To Fill beacon report for
* each beacon coming from connected peer and sends it
* to upper layer
* @mac_ctx: Mac context
* @beacon_struct: Pointing to beacon structure
* @session: pointer to the PE session
*
* Return: None
*/
static
void sch_send_beacon_report(struct mac_context *mac_ctx,
struct sSirProbeRespBeacon *beacon_struct,
struct pe_session *session)
{
struct wlan_beacon_report beacon_report;
if (!mac_ctx->lim.sme_bcn_rcv_callback)
return;
if (!LIM_IS_STA_ROLE(session))
return;
if (sir_compare_mac_addr(session->bssId, beacon_struct->bssid)) {
/* Prepare beacon report from incoming beacon */
qdf_mem_copy(beacon_report.bssid.bytes, beacon_struct->bssid,
sizeof(tSirMacAddr));
qdf_mem_copy(&beacon_report.time_stamp,
&beacon_struct->timeStamp, sizeof(qdf_time_t));
beacon_report.beacon_interval = beacon_struct->beaconInterval;
beacon_report.frequency =
cds_chan_to_freq(beacon_struct->channelNumber);
beacon_report.ssid.length = beacon_struct->ssId.length;
qdf_mem_copy(&beacon_report.ssid.ssid,
&beacon_struct->ssId.ssId,
beacon_report.ssid.length);
beacon_report.boot_time =
qdf_do_div(qdf_get_monotonic_boottime(),
QDF_MC_TIMER_TO_MS_UNIT);
/* Send report to upper layer */
mac_ctx->lim.sme_bcn_rcv_callback(mac_ctx->hdd_handle,
&beacon_report);
}
}
#else
static inline
void sch_send_beacon_report(struct mac_context *mac_ctx,
struct sSirProbeRespBeacon *beacon_struct,
struct pe_session *session)
{
}
#endif
/**
* sch_beacon_process() - process the beacon frame
* @mac_ctx: mac global context
* @rx_pkt_info: pointer to buffer descriptor
* @session: pointer to the PE session
*
* Return: None
*/
void
sch_beacon_process(struct mac_context *mac_ctx, uint8_t *rx_pkt_info,
struct pe_session *session)
{
static tSchBeaconStruct bcn;
/* Convert the beacon frame into a structure */
if (sir_convert_beacon_frame2_struct(mac_ctx, (uint8_t *) rx_pkt_info,
&bcn) != QDF_STATUS_SUCCESS) {
pe_err_rl("beacon parsing failed");
return;
}
/*
* Now process the beacon in the context of the BSS which is
* transmitting the beacons, if one is found
*/
if (!session) {
__sch_beacon_process_no_session(mac_ctx, &bcn, rx_pkt_info);
} else {
sch_send_beacon_report(mac_ctx, &bcn, session);
__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
*/
QDF_STATUS
sch_beacon_edca_process(struct mac_context *mac, tSirMacEdcaParamSetIE *edca,
struct pe_session *session)
{
uint8_t i;
#ifdef FEATURE_WLAN_DIAG_SUPPORT
host_log_qos_edca_pkt_type *log_ptr = NULL;
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
if (!(mac->mlme_cfg)) {
pe_err("invalid mlme cfg");
return QDF_STATUS_E_FAILURE;
}
pe_debug("Updating parameter set count: Old %d ---> new %d",
session->gLimEdcaParamSetCount, edca->qosInfo.count);
session->gLimEdcaParamSetCount = edca->qosInfo.count;
session->gLimEdcaParams[QCA_WLAN_AC_BE] = edca->acbe;
session->gLimEdcaParams[QCA_WLAN_AC_BK] = edca->acbk;
session->gLimEdcaParams[QCA_WLAN_AC_VI] = edca->acvi;
session->gLimEdcaParams[QCA_WLAN_AC_VO] = edca->acvo;
if (mac->mlme_cfg->edca_params.enable_edca_params) {
session->gLimEdcaParams[QCA_WLAN_AC_VO].aci.aifsn =
mac->mlme_cfg->edca_params.edca_ac_vo.vo_aifs;
session->gLimEdcaParams[QCA_WLAN_AC_VI].aci.aifsn =
mac->mlme_cfg->edca_params.edca_ac_vi.vi_aifs;
session->gLimEdcaParams[QCA_WLAN_AC_BK].aci.aifsn =
mac->mlme_cfg->edca_params.edca_ac_bk.bk_aifs;
session->gLimEdcaParams[QCA_WLAN_AC_BE].aci.aifsn =
mac->mlme_cfg->edca_params.edca_ac_be.be_aifs;
session->gLimEdcaParams[QCA_WLAN_AC_VO].cw.min =
mac->mlme_cfg->edca_params.edca_ac_vo.vo_cwmin;
session->gLimEdcaParams[QCA_WLAN_AC_VI].cw.min =
mac->mlme_cfg->edca_params.edca_ac_vi.vi_cwmin;
session->gLimEdcaParams[QCA_WLAN_AC_BK].cw.min =
mac->mlme_cfg->edca_params.edca_ac_bk.bk_cwmin;
session->gLimEdcaParams[QCA_WLAN_AC_BE].cw.min =
mac->mlme_cfg->edca_params.edca_ac_be.be_cwmin;
session->gLimEdcaParams[QCA_WLAN_AC_VO].cw.max =
mac->mlme_cfg->edca_params.edca_ac_vo.vo_cwmax;
session->gLimEdcaParams[QCA_WLAN_AC_VI].cw.max =
mac->mlme_cfg->edca_params.edca_ac_vi.vi_cwmax;
session->gLimEdcaParams[QCA_WLAN_AC_BK].cw.max =
mac->mlme_cfg->edca_params.edca_ac_bk.bk_cwmax;
session->gLimEdcaParams[QCA_WLAN_AC_BE].cw.max =
mac->mlme_cfg->edca_params.edca_ac_be.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[QCA_WLAN_AC_BE].aci.aci;
log_ptr->cw_be =
session->gLimEdcaParams[QCA_WLAN_AC_BE].cw.max << 4
| session->gLimEdcaParams[QCA_WLAN_AC_BE].cw.min;
log_ptr->txoplimit_be =
session->gLimEdcaParams[QCA_WLAN_AC_BE].txoplimit;
log_ptr->aci_bk =
session->gLimEdcaParams[QCA_WLAN_AC_BK].aci.aci;
log_ptr->cw_bk =
session->gLimEdcaParams[QCA_WLAN_AC_BK].cw.max << 4
| session->gLimEdcaParams[QCA_WLAN_AC_BK].cw.min;
log_ptr->txoplimit_bk =
session->gLimEdcaParams[QCA_WLAN_AC_BK].txoplimit;
log_ptr->aci_vi =
session->gLimEdcaParams[QCA_WLAN_AC_VI].aci.aci;
log_ptr->cw_vi =
session->gLimEdcaParams[QCA_WLAN_AC_VI].cw.max << 4
| session->gLimEdcaParams[QCA_WLAN_AC_VI].cw.min;
log_ptr->txoplimit_vi =
session->gLimEdcaParams[QCA_WLAN_AC_VI].txoplimit;
log_ptr->aci_vo =
session->gLimEdcaParams[QCA_WLAN_AC_VO].aci.aci;
log_ptr->cw_vo =
session->gLimEdcaParams[QCA_WLAN_AC_VO].cw.max << 4
| session->gLimEdcaParams[QCA_WLAN_AC_VO].cw.min;
log_ptr->txoplimit_vo =
session->gLimEdcaParams[QCA_WLAN_AC_VO].txoplimit;
}
WLAN_HOST_DIAG_LOG_REPORT(log_ptr);
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
pe_debug("Edca param enabled in ini %d. Updating Local EDCA Params(gLimEdcaParams) to: ",
mac->mlme_cfg->edca_params.enable_edca_params);
for (i = 0; i < QCA_WLAN_AC_ALL; 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 QDF_STATUS_SUCCESS;
}
void lim_enable_obss_detection_config(struct mac_context *mac_ctx,
struct pe_session *session)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (!session) {
pe_err("Invalid session, protection not enabled");
return;
}
if (session->gLimProtectionControl ==
MLME_FORCE_POLICY_PROTECTION_DISABLE) {
pe_err("protectiond disabled, force policy, session %d",
session->smeSessionId);
return;
}
if (mac_ctx->mlme_cfg->obss_ht40.obss_detection_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->mlme_cfg->obss_ht40.obss_detection_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(struct mac_context *mac_ctx,
struct pe_session *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->mlme_cfg->sta.ignore_peer_erp_info)
cfg->obss_11b_sta_detect_mode =
OBSS_OFFLOAD_DETECTION_DISABLED;
if (mac_ctx->mlme_cfg->sap_protection_cfg.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(struct mac_context *mac_ctx,
struct pe_session *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 ==
MLME_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)
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_message(QDF_MODULE_ID_PE,
QDF_MODULE_ID_WMA,
QDF_MODULE_ID_WMA, &msg);
if (QDF_IS_STATUS_ERROR(status)) {
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(struct mac_context *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;
struct pe_session *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, " QDF_MAC_ADDR_STR,
obss_detection->vdev_id, obss_detection->reason,
obss_detection->matched_detection_masks,
QDF_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 (!QDF_IS_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;
}