blob: 611df3460c97b03b7bef07cd8ae3bbc1017afbbb [file] [log] [blame]
/*
* Copyright (c) 2012-2020 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_gen.cc contains beacon generation related
* functions
*
* Author: Sandesh Goel
* Date: 02/25/02
* History:-
* Date Modified by Modification Information
* --------------------------------------------------------------------
*
*/
#include "cds_api.h"
#include "wni_cfg.h"
#include "ani_global.h"
#include "sir_mac_prot_def.h"
#include "lim_utils.h"
#include "lim_api.h"
#include "wma_if.h"
#include "sch_api.h"
#include "parser_api.h"
#include "wlan_utility.h"
/* Offset of Channel Switch count field in CSA/ECSA IE */
#define SCH_CSA_SWITCH_COUNT_OFFSET 2
#define SCH_ECSA_SWITCH_COUNT_OFFSET 3
const uint8_t p2p_oui[] = { 0x50, 0x6F, 0x9A, 0x9 };
static QDF_STATUS sch_get_p2p_ie_offset(uint8_t *pextra_ie,
uint32_t extra_ie_len,
uint16_t *pie_offset)
{
uint8_t elem_id;
uint8_t elem_len;
uint8_t *ie_ptr = pextra_ie;
uint8_t oui_size = sizeof(p2p_oui);
uint32_t p2p_ie_offset = 0;
uint32_t left_len = extra_ie_len;
QDF_STATUS status = QDF_STATUS_E_FAILURE;
*pie_offset = 0;
while (left_len > 2) {
elem_id = ie_ptr[0];
elem_len = ie_ptr[1];
left_len -= 2;
if (elem_len > left_len)
return status;
if ((elem_id == 0xDD) && (elem_len >= oui_size)) {
if (!qdf_mem_cmp(&ie_ptr[2], &p2p_oui, oui_size)) {
*pie_offset = p2p_ie_offset;
return QDF_STATUS_SUCCESS;
}
}
left_len -= elem_len;
ie_ptr += (elem_len + 2);
p2p_ie_offset += (elem_len + 2);
};
return status;
}
/**
* sch_append_addn_ie() - adds additional IEs to frame
* @mac_ctx: mac global context
* @session: pe session pointer
* @frm: frame where additional IE is to be added
* @bcn_size_left: beacon size left
* @num_bytes: final size
* @addn_ie: pointer to additional IE
* @addn_ielen: length of additional IE
*
* Return: status of operation
*/
static QDF_STATUS
sch_append_addn_ie(struct mac_context *mac_ctx, struct pe_session *session,
uint8_t *frm, uint32_t bcn_size_left,
uint32_t *num_bytes, uint8_t *addn_ie, uint16_t addn_ielen)
{
QDF_STATUS status = QDF_STATUS_E_FAILURE;
uint8_t add_ie[WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN];
uint8_t *p2p_ie = NULL;
uint8_t noa_len = 0;
uint8_t noa_strm[SIR_MAX_NOA_ATTR_LEN + SIR_P2P_IE_HEADER_LEN];
uint8_t ext_p2p_ie[DOT11F_IE_P2PBEACON_MAX_LEN + 2];
bool valid_ie;
valid_ie = (addn_ielen <= WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN &&
addn_ielen && (addn_ielen <= bcn_size_left));
if (!valid_ie)
return status;
qdf_mem_zero(&ext_p2p_ie[0], DOT11F_IE_P2PBEACON_MAX_LEN + 2);
/*
* P2P IE extracted in wlan_hdd_add_hostapd_conf_vsie may not
* be at the end of additional IE buffer. The buffer sent to WMA
* expect P2P IE at the end of beacon buffer and will result in
* beacon corruption if P2P IE is not at end of beacon buffer.
*/
status = lim_strip_ie(mac_ctx, addn_ie, &addn_ielen, WLAN_ELEMID_VENDOR,
ONE_BYTE, SIR_MAC_P2P_OUI, SIR_MAC_P2P_OUI_SIZE,
ext_p2p_ie, DOT11F_IE_P2PBEACON_MAX_LEN);
qdf_mem_copy(&add_ie[0], addn_ie, addn_ielen);
if (status == QDF_STATUS_SUCCESS &&
ext_p2p_ie[0] == WLAN_ELEMID_VENDOR &&
!qdf_mem_cmp(&ext_p2p_ie[2], SIR_MAC_P2P_OUI,
SIR_MAC_P2P_OUI_SIZE)) {
qdf_mem_copy(&add_ie[addn_ielen], ext_p2p_ie,
ext_p2p_ie[1] + 2);
addn_ielen += ext_p2p_ie[1] + 2;
}
p2p_ie = (uint8_t *)limGetP2pIEPtr(mac_ctx, &add_ie[0], addn_ielen);
if ((p2p_ie) && !mac_ctx->beacon_offload) {
/* get NoA attribute stream P2P IE */
noa_len = lim_get_noa_attr_stream(mac_ctx, noa_strm, session);
if (noa_len) {
if ((noa_len + addn_ielen) <=
WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN) {
qdf_mem_copy(&add_ie[addn_ielen], noa_strm,
noa_len);
addn_ielen += noa_len;
p2p_ie[1] += noa_len;
} else {
pe_err("Not able to insert NoA because of length constraint");
}
}
}
if (addn_ielen <= WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN) {
qdf_mem_copy(frm, &add_ie[0], addn_ielen);
*num_bytes = *num_bytes + addn_ielen;
} else {
pe_warn("Not able to insert because of len constraint %d",
addn_ielen);
}
return status;
}
/**
* sch_get_csa_ecsa_count_offset() - get the offset of Switch count field
* @ie: pointer to the beggining of IEs in the beacon frame buffer
* @ie_len: length of the IEs in the buffer
* @csa_count_offset: pointer to the csa_count_offset variable in the caller
* @ecsa_count_offset: pointer to the ecsa_count_offset variable in the caller
*
* Gets the offset of the switch count field in the CSA/ECSA IEs from the start
* of the IEs buffer.
*
* Return: None
*/
static void sch_get_csa_ecsa_count_offset(uint8_t *ie, uint32_t ie_len,
uint32_t *csa_count_offset,
uint32_t *ecsa_count_offset)
{
uint8_t *ptr = ie;
uint8_t elem_id;
uint16_t elem_len;
uint32_t offset = 0;
/* IE is not present */
if (!ie_len)
return;
while (ie_len >= 2) {
elem_id = ptr[0];
elem_len = ptr[1];
ie_len -= 2;
offset += 2;
if (elem_id == DOT11F_EID_CHANSWITCHANN &&
elem_len == 3)
*csa_count_offset = offset +
SCH_CSA_SWITCH_COUNT_OFFSET;
if (elem_id == DOT11F_EID_EXT_CHAN_SWITCH_ANN &&
elem_len == 4)
*ecsa_count_offset = offset +
SCH_ECSA_SWITCH_COUNT_OFFSET;
ie_len -= elem_len;
offset += elem_len;
ptr += (elem_len + 2);
}
}
/**
* sch_set_fixed_beacon_fields() - sets the fixed params in beacon frame
* @mac_ctx: mac global context
* @session: pe session entry
* @band: out param, band caclculated
* @opr_ch: operating channels
*
* Return: status of operation
*/
QDF_STATUS
sch_set_fixed_beacon_fields(struct mac_context *mac_ctx, struct pe_session *session)
{
tpAniBeaconStruct bcn_struct = (tpAniBeaconStruct)
session->pSchBeaconFrameBegin;
tpSirMacMgmtHdr mac;
uint16_t offset, bcn_size_left;
uint8_t *ptr;
tDot11fBeacon1 *bcn_1;
tDot11fBeacon2 *bcn_2;
uint32_t i, n_status, n_bytes;
bool wps_ap_enable = 0;
tDot11fIEWscProbeRes *wsc_prb_res;
uint8_t *extra_ie = NULL;
uint32_t extra_ie_len = 0;
uint16_t extra_ie_offset = 0;
uint16_t p2p_ie_offset = 0;
uint32_t csa_count_offset = 0;
uint32_t ecsa_count_offset = 0;
QDF_STATUS status = QDF_STATUS_SUCCESS;
bool is_vht_enabled = false;
uint16_t addn_ielen = 0;
uint8_t *addn_ie = NULL;
tDot11fIEExtCap extracted_extcap;
bool extcap_present = true, addnie_present = false;
bool is_6ghz_chsw;
bcn_1 = qdf_mem_malloc(sizeof(tDot11fBeacon1));
if (!bcn_1)
return QDF_STATUS_E_NOMEM;
bcn_2 = qdf_mem_malloc(sizeof(tDot11fBeacon2));
if (!bcn_2) {
qdf_mem_free(bcn_1);
return QDF_STATUS_E_NOMEM;
}
wsc_prb_res = qdf_mem_malloc(sizeof(tDot11fIEWscProbeRes));
if (!wsc_prb_res) {
qdf_mem_free(bcn_1);
qdf_mem_free(bcn_2);
return QDF_STATUS_E_NOMEM;
}
pe_debug("Setting fixed beacon fields");
/*
* First set the fixed fields:
* set the TFP headers, set the mac header
*/
qdf_mem_zero((uint8_t *) &bcn_struct->macHdr, sizeof(tSirMacMgmtHdr));
mac = (tpSirMacMgmtHdr) &bcn_struct->macHdr;
mac->fc.type = SIR_MAC_MGMT_FRAME;
mac->fc.subType = SIR_MAC_MGMT_BEACON;
for (i = 0; i < 6; i++)
mac->da[i] = 0xff;
qdf_mem_copy(mac->sa, session->self_mac_addr,
sizeof(session->self_mac_addr));
qdf_mem_copy(mac->bssId, session->bssId, sizeof(session->bssId));
mac->fc.fromDS = 0;
mac->fc.toDS = 0;
/* Skip over the timestamp (it'll be updated later). */
bcn_1->BeaconInterval.interval =
session->beaconParams.beaconInterval;
populate_dot11f_capabilities(mac_ctx, &bcn_1->Capabilities, session);
if (session->ssidHidden) {
bcn_1->SSID.present = 1;
/* rest of the fileds are 0 for hidden ssid */
if ((session->ssId.length) &&
(session->ssidHidden == eHIDDEN_SSID_ZERO_CONTENTS))
bcn_1->SSID.num_ssid = session->ssId.length;
} else {
populate_dot11f_ssid(mac_ctx, &session->ssId, &bcn_1->SSID);
}
populate_dot11f_supp_rates(mac_ctx, POPULATE_DOT11F_RATES_OPERATIONAL,
&bcn_1->SuppRates, session);
populate_dot11f_ds_params(mac_ctx, &bcn_1->DSParams,
wlan_reg_freq_to_chan(
mac_ctx->pdev, session->curr_op_freq));
populate_dot11f_ibss_params(mac_ctx, &bcn_1->IBSSParams, session);
offset = sizeof(tAniBeaconStruct);
ptr = session->pSchBeaconFrameBegin + offset;
if (LIM_IS_AP_ROLE(session)) {
/* Initialize the default IE bitmap to zero */
qdf_mem_zero((uint8_t *) &(session->DefProbeRspIeBitmap),
(sizeof(uint32_t) * 8));
/* Initialize the default IE bitmap to zero */
qdf_mem_zero((uint8_t *) &(session->probeRespFrame),
sizeof(session->probeRespFrame));
/*
* Can be efficiently updated whenever new IE added in Probe
* response in future
*/
if (lim_update_probe_rsp_template_ie_bitmap_beacon1(mac_ctx,
bcn_1, session) != QDF_STATUS_SUCCESS)
pe_err("Failed to build ProbeRsp template");
}
n_status = dot11f_pack_beacon1(mac_ctx, bcn_1, ptr,
SIR_MAX_BEACON_SIZE - offset, &n_bytes);
if (DOT11F_FAILED(n_status)) {
pe_err("Failed to packed a tDot11fBeacon1 (0x%08x)",
n_status);
qdf_mem_free(bcn_1);
qdf_mem_free(bcn_2);
qdf_mem_free(wsc_prb_res);
return QDF_STATUS_E_FAILURE;
} else if (DOT11F_WARNED(n_status)) {
pe_warn("Warnings while packing a tDot11fBeacon1(0x%08x)",
n_status);
}
session->schBeaconOffsetBegin = offset + (uint16_t) n_bytes;
pe_debug("Initialized beacon begin, offset %d fixed size %d", offset,
session->schBeaconOffsetBegin);
/* Initialize the 'new' fields at the end of the beacon */
is_6ghz_chsw =
WLAN_REG_IS_6GHZ_CHAN_FREQ(session->curr_op_freq) ||
WLAN_REG_IS_6GHZ_CHAN_FREQ
(session->gLimChannelSwitch.sw_target_freq);
if (session->limSystemRole == eLIM_AP_ROLE &&
session->dfsIncludeChanSwIe == true) {
if (!CHAN_HOP_ALL_BANDS_ENABLE ||
session->lim_non_ecsa_cap_num == 0 || is_6ghz_chsw) {
tDot11fIEext_chan_switch_ann *ext_csa =
&bcn_2->ext_chan_switch_ann;
populate_dot_11_f_ext_chann_switch_ann(mac_ctx,
ext_csa,
session);
pe_debug("ecsa: mode:%d reg:%d chan:%d count:%d",
ext_csa->switch_mode,
ext_csa->new_reg_class,
ext_csa->new_channel,
ext_csa->switch_count);
} else {
populate_dot11f_chan_switch_ann(mac_ctx,
&bcn_2->ChanSwitchAnn,
session);
pe_debug("csa: mode:%d chan:%d count:%d",
bcn_2->ChanSwitchAnn.switchMode,
bcn_2->ChanSwitchAnn.newChannel,
bcn_2->ChanSwitchAnn.switchCount);
}
}
populate_dot11_supp_operating_classes(mac_ctx,
&bcn_2->SuppOperatingClasses, session);
populate_dot11f_country(mac_ctx, &bcn_2->Country, session);
if (bcn_1->Capabilities.qos)
populate_dot11f_edca_param_set(mac_ctx, &bcn_2->EDCAParamSet,
session);
if (session->lim11hEnable) {
populate_dot11f_power_constraints(mac_ctx,
&bcn_2->PowerConstraints);
populate_dot11f_tpc_report(mac_ctx, &bcn_2->TPCReport, session);
/* Need to insert channel switch announcement here */
if ((LIM_IS_AP_ROLE(session)
|| LIM_IS_P2P_DEVICE_GO(session))
&& session->dfsIncludeChanSwIe == true) {
/*
* Channel switch announcement only if radar is detected
* and SAP has instructed to announce channel switch IEs
* in beacon and probe responses
*/
if (!is_6ghz_chsw) {
populate_dot11f_chan_switch_ann
(mac_ctx, &bcn_2->ChanSwitchAnn,
session);
pe_debug("csa: mode:%d chan:%d count:%d",
bcn_2->ChanSwitchAnn.switchMode,
bcn_2->ChanSwitchAnn.newChannel,
bcn_2->ChanSwitchAnn.switchCount);
}
/*
* TODO: depending the CB mode, extended channel switch
* announcement need to be called
*/
/*
populate_dot11f_ext_chan_switch_ann(mac_ctx,
&bcn_2->ExtChanSwitchAnn, session);
*/
/*
* TODO: If in 11AC mode, wider bw channel switch
* announcement needs to be called
*/
/*
populate_dot11f_wider_bw_chan_switch_ann(mac_ctx,
&bcn_2->WiderBWChanSwitchAnn, session);
*/
/*
* Populate the Channel Switch Wrapper Element if
* SAP operates in 40/80 Mhz Channel Width.
*/
if (true == session->dfsIncludeChanWrapperIe) {
populate_dot11f_chan_switch_wrapper(mac_ctx,
&bcn_2->ChannelSwitchWrapper, session);
pe_debug("wrapper: width:%d f0:%d f1:%d",
bcn_2->ChannelSwitchWrapper.
WiderBWChanSwitchAnn.newChanWidth,
bcn_2->ChannelSwitchWrapper.
WiderBWChanSwitchAnn.newCenterChanFreq0,
bcn_2->ChannelSwitchWrapper.
WiderBWChanSwitchAnn.newCenterChanFreq1
);
}
}
}
if (mac_ctx->rrm.rrmConfig.rrm_enabled)
populate_dot11f_rrm_ie(mac_ctx, &bcn_2->RRMEnabledCap,
session);
#ifdef FEATURE_AP_MCC_CH_AVOIDANCE
/* populate proprietary IE for MDM device operating in AP-MCC */
populate_dot11f_avoid_channel_ie(mac_ctx, &bcn_2->QComVendorIE,
session);
#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */
if (session->dot11mode != MLME_DOT11_MODE_11B)
populate_dot11f_erp_info(mac_ctx, &bcn_2->ERPInfo, session);
if (session->htCapability) {
populate_dot11f_ht_caps(mac_ctx, session, &bcn_2->HTCaps);
populate_dot11f_ht_info(mac_ctx, &bcn_2->HTInfo, session);
}
if (session->vhtCapability) {
populate_dot11f_vht_caps(mac_ctx, session, &bcn_2->VHTCaps);
populate_dot11f_vht_operation(mac_ctx, session,
&bcn_2->VHTOperation);
is_vht_enabled = true;
/* following is for MU MIMO: we do not support it yet */
/*
populate_dot11f_vht_ext_bss_load( mac_ctx, &bcn2.VHTExtBssLoad);
*/
populate_dot11f_vht_tx_power_env(mac_ctx,
&bcn_2->vht_transmit_power_env,
session->ch_width,
session->curr_op_freq);
populate_dot11f_qcn_ie(mac_ctx, &bcn_2->qcn_ie,
QCN_IE_ATTR_ID_ALL);
}
if (lim_is_session_he_capable(session)) {
pe_debug("Populate HE IEs");
populate_dot11f_he_caps(mac_ctx, session,
&bcn_2->he_cap);
populate_dot11f_he_operation(mac_ctx, session,
&bcn_2->he_op);
populate_dot11f_he_6ghz_cap(mac_ctx, session,
&bcn_2->he_6ghz_band_cap);
populate_dot11f_he_bss_color_change(mac_ctx, session,
&bcn_2->bss_color_change);
}
if (session->limSystemRole != eLIM_STA_IN_IBSS_ROLE)
populate_dot11f_ext_cap(mac_ctx, is_vht_enabled, &bcn_2->ExtCap,
session);
populate_dot11f_ext_supp_rates(mac_ctx,
POPULATE_DOT11F_RATES_OPERATIONAL,
&bcn_2->ExtSuppRates, session);
if (session->pLimStartBssReq) {
populate_dot11f_wpa(mac_ctx, &session->pLimStartBssReq->rsnIE,
&bcn_2->WPA);
populate_dot11f_rsn_opaque(mac_ctx,
&session->pLimStartBssReq->rsnIE,
&bcn_2->RSNOpaque);
}
if (session->limWmeEnabled)
populate_dot11f_wmm(mac_ctx, &bcn_2->WMMInfoAp,
&bcn_2->WMMParams, &bcn_2->WMMCaps, session);
if (LIM_IS_AP_ROLE(session)) {
if (session->wps_state != SAP_WPS_DISABLED) {
populate_dot11f_beacon_wpsi_es(mac_ctx,
&bcn_2->WscBeacon, session);
}
} else {
wps_ap_enable = mac_ctx->mlme_cfg->wps_params.enable_wps &
WNI_CFG_WPS_ENABLE_AP;
if (wps_ap_enable)
populate_dot11f_wsc(mac_ctx, &bcn_2->WscBeacon);
if (mac_ctx->lim.wscIeInfo.wscEnrollmentState ==
eLIM_WSC_ENROLL_BEGIN) {
populate_dot11f_wsc_registrar_info(mac_ctx,
&bcn_2->WscBeacon);
mac_ctx->lim.wscIeInfo.wscEnrollmentState =
eLIM_WSC_ENROLL_IN_PROGRESS;
}
if (mac_ctx->lim.wscIeInfo.wscEnrollmentState ==
eLIM_WSC_ENROLL_END) {
de_populate_dot11f_wsc_registrar_info(mac_ctx,
&bcn_2->WscBeacon);
mac_ctx->lim.wscIeInfo.wscEnrollmentState =
eLIM_WSC_ENROLL_NOOP;
}
}
if (LIM_IS_AP_ROLE(session)) {
/*
* Can be efficiently updated whenever new IE added in Probe
* response in future
*/
lim_update_probe_rsp_template_ie_bitmap_beacon2(mac_ctx, bcn_2,
&session->DefProbeRspIeBitmap[0],
&session->probeRespFrame);
/* update probe response WPS IE instead of beacon WPS IE */
if (session->wps_state != SAP_WPS_DISABLED) {
if (session->APWPSIEs.SirWPSProbeRspIE.FieldPresent)
populate_dot11f_probe_res_wpsi_es(mac_ctx,
wsc_prb_res, session);
else
wsc_prb_res->present = 0;
if (wsc_prb_res->present) {
set_probe_rsp_ie_bitmap(
&session->DefProbeRspIeBitmap[0],
SIR_MAC_WPA_EID);
qdf_mem_copy((void *)
&session->probeRespFrame.WscProbeRes,
(void *)wsc_prb_res,
sizeof(tDot11fIEWscProbeRes));
}
}
}
addnie_present = (session->add_ie_params.probeRespBCNDataLen != 0);
if (addnie_present) {
addn_ielen = session->add_ie_params.probeRespBCNDataLen;
addn_ie = qdf_mem_malloc(addn_ielen);
if (!addn_ie) {
qdf_mem_free(bcn_1);
qdf_mem_free(bcn_2);
qdf_mem_free(wsc_prb_res);
return QDF_STATUS_E_NOMEM;
}
qdf_mem_copy(addn_ie,
session->add_ie_params.probeRespBCNData_buff,
addn_ielen);
qdf_mem_zero((uint8_t *)&extracted_extcap,
sizeof(tDot11fIEExtCap));
status = lim_strip_extcap_update_struct(mac_ctx, addn_ie,
&addn_ielen, &extracted_extcap);
if (QDF_STATUS_SUCCESS != status) {
extcap_present = false;
pe_debug("extcap not extracted");
}
/* merge extcap IE */
if (extcap_present &&
session->limSystemRole != eLIM_STA_IN_IBSS_ROLE)
lim_merge_extcap_struct(&bcn_2->ExtCap,
&extracted_extcap,
true);
}
if (session->vhtCapability && session->gLimOperatingMode.present) {
populate_dot11f_operating_mode(mac_ctx, &bcn_2->OperatingMode,
session);
lim_strip_ie(mac_ctx, addn_ie, &addn_ielen,
WLAN_ELEMID_OP_MODE_NOTIFY, ONE_BYTE, NULL, 0,
NULL, SIR_MAC_VHT_OPMODE_SIZE - 2);
}
n_status = dot11f_pack_beacon2(mac_ctx, bcn_2,
session->pSchBeaconFrameEnd,
SIR_MAX_BEACON_SIZE, &n_bytes);
if (DOT11F_FAILED(n_status)) {
pe_err("Failed to packed a tDot11fBeacon2 (0x%08x)",
n_status);
qdf_mem_free(bcn_1);
qdf_mem_free(bcn_2);
qdf_mem_free(wsc_prb_res);
qdf_mem_free(addn_ie);
return QDF_STATUS_E_FAILURE;
} else if (DOT11F_WARNED(n_status)) {
pe_err("Warnings while packing a tDot11fBeacon2(0x%08x)",
n_status);
}
/* Fill the CSA/ECSA count offsets if the IEs are present */
mac_ctx->sch.ecsa_count_offset = 0;
mac_ctx->sch.csa_count_offset = 0;
if (session->dfsIncludeChanSwIe)
sch_get_csa_ecsa_count_offset(session->pSchBeaconFrameEnd,
n_bytes,
&csa_count_offset,
&ecsa_count_offset);
if (csa_count_offset)
mac_ctx->sch.csa_count_offset =
session->schBeaconOffsetBegin + TIM_IE_SIZE +
csa_count_offset;
if (ecsa_count_offset)
mac_ctx->sch.ecsa_count_offset =
session->schBeaconOffsetBegin + TIM_IE_SIZE +
ecsa_count_offset;
pe_debug("csa_count_offset %d ecsa_count_offset %d",
mac_ctx->sch.csa_count_offset,
mac_ctx->sch.ecsa_count_offset);
extra_ie = session->pSchBeaconFrameEnd + n_bytes;
extra_ie_offset = n_bytes;
/*
* Max size left to append additional IE.= (MAX beacon size - TIM IE -
* beacon fix size (bcn_1 + header) - beacon variable size (bcn_1).
*/
bcn_size_left = SIR_MAX_BEACON_SIZE - TIM_IE_SIZE -
session->schBeaconOffsetBegin -
(uint16_t)n_bytes;
pe_debug("max_bcn_size_left %d and addn_ielen %d", bcn_size_left,
addn_ielen);
/* TODO: Append additional IE here. */
if (addn_ielen > 0)
sch_append_addn_ie(mac_ctx, session,
session->pSchBeaconFrameEnd + n_bytes,
bcn_size_left, &n_bytes,
addn_ie, addn_ielen);
session->schBeaconOffsetEnd = (uint16_t) n_bytes;
extra_ie_len = n_bytes - extra_ie_offset;
/* Get the p2p Ie Offset */
status = sch_get_p2p_ie_offset(extra_ie, extra_ie_len, &p2p_ie_offset);
if (QDF_STATUS_SUCCESS == status)
/* Update the P2P Ie Offset */
mac_ctx->sch.p2p_ie_offset =
session->schBeaconOffsetBegin + TIM_IE_SIZE +
extra_ie_offset + p2p_ie_offset;
else
mac_ctx->sch.p2p_ie_offset = 0;
pe_debug("Initialized beacon end, offset %d",
session->schBeaconOffsetEnd);
mac_ctx->sch.beacon_changed = 1;
qdf_mem_free(bcn_1);
qdf_mem_free(bcn_2);
qdf_mem_free(wsc_prb_res);
qdf_mem_free(addn_ie);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
lim_update_probe_rsp_template_ie_bitmap_beacon1(struct mac_context *mac,
tDot11fBeacon1 *beacon1,
struct pe_session *pe_session)
{
uint32_t *DefProbeRspIeBitmap;
tDot11fProbeResponse *prb_rsp;
if (!pe_session) {
pe_debug("PESession is null!");
return QDF_STATUS_E_FAILURE;
}
DefProbeRspIeBitmap = &pe_session->DefProbeRspIeBitmap[0];
prb_rsp = &pe_session->probeRespFrame;
prb_rsp->BeaconInterval = beacon1->BeaconInterval;
qdf_mem_copy((void *)&prb_rsp->Capabilities,
(void *)&beacon1->Capabilities,
sizeof(beacon1->Capabilities));
/* SSID */
if (beacon1->SSID.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, WLAN_ELEMID_SSID);
/* populating it, because probe response has to go with SSID even in hidden case */
populate_dot11f_ssid(mac, &pe_session->ssId, &prb_rsp->SSID);
}
/* supported rates */
if (beacon1->SuppRates.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, WLAN_ELEMID_RATES);
qdf_mem_copy((void *)&prb_rsp->SuppRates,
(void *)&beacon1->SuppRates,
sizeof(beacon1->SuppRates));
}
/* DS Parameter set */
if (beacon1->DSParams.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
WLAN_ELEMID_DSPARMS);
qdf_mem_copy((void *)&prb_rsp->DSParams,
(void *)&beacon1->DSParams,
sizeof(beacon1->DSParams));
}
/* IBSS params will not be present in the Beacons transmitted by AP */
return QDF_STATUS_SUCCESS;
}
void lim_update_probe_rsp_template_ie_bitmap_beacon2(struct mac_context *mac,
tDot11fBeacon2 *beacon2,
uint32_t *DefProbeRspIeBitmap,
tDot11fProbeResponse *prb_rsp)
{
/* IBSS parameter set - will not be present in probe response tx by AP */
/* country */
if (beacon2->Country.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, WLAN_ELEMID_COUNTRY);
qdf_mem_copy((void *)&prb_rsp->Country,
(void *)&beacon2->Country,
sizeof(beacon2->Country));
}
/* Power constraint */
if (beacon2->PowerConstraints.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
WLAN_ELEMID_PWRCNSTR);
qdf_mem_copy((void *)&prb_rsp->PowerConstraints,
(void *)&beacon2->PowerConstraints,
sizeof(beacon2->PowerConstraints));
}
/* Channel Switch Annoouncement WLAN_ELEMID_CHANSWITCHANN */
if (beacon2->ChanSwitchAnn.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
WLAN_ELEMID_CHANSWITCHANN);
qdf_mem_copy((void *)&prb_rsp->ChanSwitchAnn,
(void *)&beacon2->ChanSwitchAnn,
sizeof(beacon2->ChanSwitchAnn));
}
/* EXT Channel Switch Announcement CHNL_EXTENDED_SWITCH_ANN_EID*/
if (beacon2->ext_chan_switch_ann.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
WLAN_ELEMID_EXTCHANSWITCHANN);
qdf_mem_copy((void *)&prb_rsp->ext_chan_switch_ann,
(void *)&beacon2->ext_chan_switch_ann,
sizeof(beacon2->ext_chan_switch_ann));
}
/* Supported operating class */
if (beacon2->SuppOperatingClasses.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
WLAN_ELEMID_SUPP_OP_CLASS);
qdf_mem_copy((void *)&prb_rsp->SuppOperatingClasses,
(void *)&beacon2->SuppOperatingClasses,
sizeof(beacon2->SuppOperatingClasses));
}
#ifdef FEATURE_AP_MCC_CH_AVOIDANCE
if (beacon2->QComVendorIE.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
SIR_MAC_QCOM_VENDOR_EID);
qdf_mem_copy((void *)&prb_rsp->QComVendorIE,
(void *)&beacon2->QComVendorIE,
sizeof(beacon2->QComVendorIE));
}
#endif /* FEATURE_AP_MCC_CH_AVOIDANCE */
/* ERP information */
if (beacon2->ERPInfo.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, WLAN_ELEMID_ERP);
qdf_mem_copy((void *)&prb_rsp->ERPInfo,
(void *)&beacon2->ERPInfo,
sizeof(beacon2->ERPInfo));
}
/* Extended supported rates */
if (beacon2->ExtSuppRates.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
WLAN_ELEMID_XRATES);
qdf_mem_copy((void *)&prb_rsp->ExtSuppRates,
(void *)&beacon2->ExtSuppRates,
sizeof(beacon2->ExtSuppRates));
}
/* WPA */
if (beacon2->WPA.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, SIR_MAC_WPA_EID);
qdf_mem_copy((void *)&prb_rsp->WPA, (void *)&beacon2->WPA,
sizeof(beacon2->WPA));
}
/* RSN */
if (beacon2->RSNOpaque.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, WLAN_ELEMID_RSN);
qdf_mem_copy((void *)&prb_rsp->RSNOpaque,
(void *)&beacon2->RSNOpaque,
sizeof(beacon2->RSNOpaque));
}
/* EDCA Parameter set */
if (beacon2->EDCAParamSet.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
WLAN_ELEMID_EDCAPARMS);
qdf_mem_copy((void *)&prb_rsp->EDCAParamSet,
(void *)&beacon2->EDCAParamSet,
sizeof(beacon2->EDCAParamSet));
}
/* Vendor specific - currently no vendor specific IEs added */
/* Requested IEs - currently we are not processing this will be added later */
/* HT capability IE */
if (beacon2->HTCaps.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
WLAN_ELEMID_HTCAP_ANA);
qdf_mem_copy((void *)&prb_rsp->HTCaps, (void *)&beacon2->HTCaps,
sizeof(beacon2->HTCaps));
}
/* HT Info IE */
if (beacon2->HTInfo.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, WLAN_ELEMID_HTINFO_ANA);
qdf_mem_copy((void *)&prb_rsp->HTInfo, (void *)&beacon2->HTInfo,
sizeof(beacon2->HTInfo));
}
if (beacon2->VHTCaps.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
WLAN_ELEMID_VHTCAP);
qdf_mem_copy((void *)&prb_rsp->VHTCaps,
(void *)&beacon2->VHTCaps,
sizeof(beacon2->VHTCaps));
}
if (beacon2->VHTOperation.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
WLAN_ELEMID_VHTOP);
qdf_mem_copy((void *)&prb_rsp->VHTOperation,
(void *)&beacon2->VHTOperation,
sizeof(beacon2->VHTOperation));
}
if (beacon2->VHTExtBssLoad.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
WLAN_ELEMID_EXT_BSS_LOAD);
qdf_mem_copy((void *)&prb_rsp->VHTExtBssLoad,
(void *)&beacon2->VHTExtBssLoad,
sizeof(beacon2->VHTExtBssLoad));
}
/* WMM IE */
if (beacon2->WMMParams.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, SIR_MAC_WPA_EID);
qdf_mem_copy((void *)&prb_rsp->WMMParams,
(void *)&beacon2->WMMParams,
sizeof(beacon2->WMMParams));
}
/* WMM capability - most of the case won't be present */
if (beacon2->WMMCaps.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, SIR_MAC_WPA_EID);
qdf_mem_copy((void *)&prb_rsp->WMMCaps,
(void *)&beacon2->WMMCaps,
sizeof(beacon2->WMMCaps));
}
/* Extended Capability */
if (beacon2->ExtCap.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap, DOT11F_EID_EXTCAP);
qdf_mem_copy((void *)&prb_rsp->ExtCap,
(void *)&beacon2->ExtCap,
sizeof(beacon2->ExtCap));
}
if (beacon2->he_cap.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
DOT11F_EID_HE_CAP);
qdf_mem_copy((void *)&prb_rsp->he_cap,
(void *)&beacon2->he_cap,
sizeof(beacon2->he_cap));
}
if (beacon2->he_op.present) {
set_probe_rsp_ie_bitmap(DefProbeRspIeBitmap,
DOT11F_EID_HE_OP);
qdf_mem_copy((void *)&prb_rsp->he_op,
(void *)&beacon2->he_op,
sizeof(beacon2->he_op));
}
}
void set_probe_rsp_ie_bitmap(uint32_t *IeBitmap, uint32_t pos)
{
uint32_t index, temp;
index = pos >> 5;
if (index >= 8) {
return;
}
temp = IeBitmap[index];
temp |= 1 << (pos & 0x1F);
IeBitmap[index] = temp;
}
/**
* write_beacon_to_memory() - send the beacon to the wma
* @mac: pointer to mac structure
* @size: Size of the beacon to write to memory
* @length: Length field of the beacon to write to memory
* @pe_session: pe session
* @reason: beacon update reason
*
* return: success: QDF_STATUS_SUCCESS failure: QDF_STATUS_E_FAILURE
*/
static QDF_STATUS write_beacon_to_memory(struct mac_context *mac, uint16_t size,
uint16_t length,
struct pe_session *pe_session,
enum sir_bcn_update_reason reason)
{
uint16_t i;
tpAniBeaconStruct pBeacon;
QDF_STATUS status;
/* copy end of beacon only if length > 0 */
if (length > 0) {
if (size + pe_session->schBeaconOffsetEnd >
SIR_MAX_BEACON_SIZE) {
pe_err("beacon tmp fail size %d BeaconOffsetEnd %d",
size, pe_session->schBeaconOffsetEnd);
return QDF_STATUS_E_FAILURE;
}
for (i = 0; i < pe_session->schBeaconOffsetEnd; i++)
pe_session->pSchBeaconFrameBegin[size++] =
pe_session->pSchBeaconFrameEnd[i];
}
/* Update the beacon length */
pBeacon = (tpAniBeaconStruct) pe_session->pSchBeaconFrameBegin;
/* Do not include the beaconLength indicator itself */
if (length == 0) {
pBeacon->beaconLength = 0;
/* Dont copy entire beacon, Copy length field alone */
size = 4;
} else
pBeacon->beaconLength = (uint32_t) size - sizeof(uint32_t);
if (!mac->sch.beacon_changed)
return QDF_STATUS_E_FAILURE;
status = sch_send_beacon_req(mac, pe_session->pSchBeaconFrameBegin,
size, pe_session, reason);
if (QDF_IS_STATUS_ERROR(status))
pe_err("sch_send_beacon_req() returned an error %d, size %d",
status, size);
mac->sch.beacon_changed = 0;
return status;
}
/**
* sch_generate_tim
*
* FUNCTION:
* Generate TIM
*
* LOGIC:
*
* ASSUMPTIONS:
*
* NOTE:
*
* @param mac pointer to global mac structure
* @param **pPtr pointer to the buffer, where the TIM bit is to be written.
* @param *timLength pointer to limLength, which needs to be returned.
* @return None
*/
void sch_generate_tim(struct mac_context *mac, uint8_t **pPtr, uint16_t *timLength,
uint8_t dtimPeriod)
{
uint8_t *ptr = *pPtr;
uint32_t val = 0;
uint32_t minAid = 1; /* Always start with AID 1 as minimum */
uint32_t maxAid = HAL_NUM_STA;
/* Generate partial virtual bitmap */
uint8_t N1 = minAid / 8;
uint8_t N2 = maxAid / 8;
if (N1 & 1)
N1--;
*timLength = N2 - N1 + 4;
val = dtimPeriod;
/*
* Write 0xFF to firmware's field to detect firmware's mal-function
* early. DTIM count and bitmap control usually cannot be 0xFF, so it
* is easy to know that firmware never updated DTIM count/bitmap control
* field after host driver downloaded beacon template if end-user complaints
* that DTIM count and bitmapControl is 0xFF.
*/
*ptr++ = WLAN_ELEMID_TIM;
*ptr++ = (uint8_t) (*timLength);
/* location for dtimCount. will be filled in by FW. */
*ptr++ = 0xFF;
*ptr++ = (uint8_t) val;
/* location for bitmap control. will be filled in by FW. */
*ptr++ = 0xFF;
ptr += (N2 - N1 + 1);
*pPtr = ptr;
}
QDF_STATUS sch_process_pre_beacon_ind(struct mac_context *mac,
struct scheduler_msg *limMsg,
enum sir_bcn_update_reason reason)
{
struct beacon_gen_params *pMsg = limMsg->bodyptr;
uint32_t beaconSize;
struct pe_session *pe_session;
uint8_t sessionId;
QDF_STATUS status = QDF_STATUS_E_FAILURE;
pe_session = pe_find_session_by_bssid(mac, pMsg->bssid, &sessionId);
if (!pe_session) {
pe_err("session lookup fails");
goto end;
}
beaconSize = pe_session->schBeaconOffsetBegin;
/* If SME is not in normal mode, no need to generate beacon */
if (pe_session->limSmeState != eLIM_SME_NORMAL_STATE) {
pe_debug("PreBeaconInd received in invalid state: %d",
pe_session->limSmeState);
goto end;
}
switch (GET_LIM_SYSTEM_ROLE(pe_session)) {
case eLIM_STA_IN_IBSS_ROLE:
/* generate IBSS parameter set */
if (pe_session->statypeForBss == STA_ENTRY_SELF)
status =
write_beacon_to_memory(mac, (uint16_t) beaconSize,
(uint16_t) beaconSize,
pe_session, reason);
else
pe_err("can not send beacon for PEER session entry");
break;
case eLIM_AP_ROLE: {
uint8_t *ptr =
&pe_session->pSchBeaconFrameBegin[pe_session->
schBeaconOffsetBegin];
uint16_t timLength = 0;
if (pe_session->statypeForBss == STA_ENTRY_SELF) {
sch_generate_tim(mac, &ptr, &timLength,
pe_session->dtimPeriod);
beaconSize += 2 + timLength;
status =
write_beacon_to_memory(mac, (uint16_t) beaconSize,
(uint16_t) beaconSize,
pe_session, reason);
} else
pe_err("can not send beacon for PEER session entry");
}
break;
default:
pe_err("Error-PE has Receive PreBeconGenIndication when System is in %d role",
GET_LIM_SYSTEM_ROLE(pe_session));
}
end:
qdf_mem_free(pMsg);
return status;
}