blob: c75cd561a7cd28b13877189116753cc91a5f9fa5 [file] [log] [blame]
/*
* Copyright (c) 2012-2018 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.
*/
#include "cds_api.h"
#include "sir_common.h"
#include "wni_cfg.h"
#include "ani_global.h"
#include "cfg_api.h"
#include "lim_api.h"
#include "lim_send_messages.h"
#include "sch_api.h"
/* / Minimum beacon interval allowed (in Kus) */
#define SCH_BEACON_INTERVAL_MIN 10
/* / Maximum beacon interval allowed (in Kus) */
#define SCH_BEACON_INTERVAL_MAX 10000
/* / convert the CW values into a uint16_t */
#define GET_CW(pCw) ((uint16_t) ((*(pCw) << 8) + *((pCw) + 1)))
/* local functions */
static QDF_STATUS
get_wmm_local_params(tpAniSirGlobal pMac,
uint32_t params[][WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN]);
static void
set_sch_edca_params(tpAniSirGlobal pMac,
uint32_t params[][WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN],
tpPESession psessionEntry);
/* -------------------------------------------------------------------- */
/**
* sch_set_beacon_interval
*
* FUNCTION:
*
* LOGIC:
*
* ASSUMPTIONS:
*
* NOTE:
*
* @param None
* @return None
*/
void sch_set_beacon_interval(tpAniSirGlobal pMac, tpPESession psessionEntry)
{
uint32_t bi;
bi = psessionEntry->beaconParams.beaconInterval;
if (bi < SCH_BEACON_INTERVAL_MIN || bi > SCH_BEACON_INTERVAL_MAX) {
pe_debug("Invalid beacon interval %d (should be [%d,%d]", bi,
SCH_BEACON_INTERVAL_MIN, SCH_BEACON_INTERVAL_MAX);
return;
}
pMac->sch.schObject.gSchBeaconInterval = (uint16_t) bi;
}
static void sch_edca_profile_update_all(tpAniSirGlobal pmac)
{
uint32_t i;
tpPESession psession_entry;
for (i = 0; i < pmac->lim.maxBssId; i++) {
psession_entry = &pmac->lim.gpSession[i];
if (psession_entry->valid)
sch_edca_profile_update(pmac, psession_entry);
}
}
/* -------------------------------------------------------------------- */
/**
* sch_process_message
*
* FUNCTION:
*
* LOGIC:
*
* ASSUMPTIONS:
*
* NOTE:
*
* @param None
* @return None
*/
void sch_process_message(tpAniSirGlobal pMac, struct scheduler_msg *pSchMsg)
{
tpPESession psessionEntry = &pMac->lim.gpSession[0];
switch (pSchMsg->type) {
case SIR_CFG_PARAM_UPDATE_IND:
switch (pSchMsg->bodyval) {
case WNI_CFG_COUNTRY_CODE:
pe_debug("sch: WNI_CFG_COUNTRY_CODE changed");
sch_edca_profile_update_all(pMac);
break;
case WNI_CFG_EDCA_PROFILE:
sch_edca_profile_update(pMac, psessionEntry);
break;
case WNI_CFG_EDCA_ANI_ACBK_LOCAL:
case WNI_CFG_EDCA_ANI_ACBE_LOCAL:
case WNI_CFG_EDCA_ANI_ACVI_LOCAL:
case WNI_CFG_EDCA_ANI_ACVO_LOCAL:
case WNI_CFG_EDCA_WME_ACBK_LOCAL:
case WNI_CFG_EDCA_WME_ACBE_LOCAL:
case WNI_CFG_EDCA_WME_ACVI_LOCAL:
case WNI_CFG_EDCA_WME_ACVO_LOCAL:
if (LIM_IS_AP_ROLE(psessionEntry))
sch_qos_update_local(pMac, psessionEntry);
break;
case WNI_CFG_EDCA_ANI_ACBK:
case WNI_CFG_EDCA_ANI_ACBE:
case WNI_CFG_EDCA_ANI_ACVI:
case WNI_CFG_EDCA_ANI_ACVO:
case WNI_CFG_EDCA_WME_ACBK:
case WNI_CFG_EDCA_WME_ACBE:
case WNI_CFG_EDCA_WME_ACVI:
case WNI_CFG_EDCA_WME_ACVO:
if (LIM_IS_AP_ROLE(psessionEntry)) {
sch_qos_update_broadcast(pMac, psessionEntry);
}
break;
default:
pe_err("Cfg param %d indication not handled",
pSchMsg->bodyval);
}
break;
default:
pe_err("Unknown message in schMsgQ type %d", pSchMsg->type);
}
}
/* get the local or broadcast parameters based on the profile sepcified in the config */
/* params are delivered in this order: BK, BE, VI, VO */
static QDF_STATUS
sch_get_params(tpAniSirGlobal pMac,
uint32_t params[][WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN],
uint8_t local)
{
uint32_t val;
uint32_t i, idx;
uint32_t *prf;
uint8_t country_code_str[WNI_CFG_COUNTRY_CODE_LEN];
uint32_t country_code_len = WNI_CFG_COUNTRY_CODE_LEN;
uint32_t ani_l[] = {
WNI_CFG_EDCA_ANI_ACBE_LOCAL, WNI_CFG_EDCA_ANI_ACBK_LOCAL,
WNI_CFG_EDCA_ANI_ACVI_LOCAL, WNI_CFG_EDCA_ANI_ACVO_LOCAL};
uint32_t wme_l[] = {
WNI_CFG_EDCA_WME_ACBE_LOCAL, WNI_CFG_EDCA_WME_ACBK_LOCAL,
WNI_CFG_EDCA_WME_ACVI_LOCAL, WNI_CFG_EDCA_WME_ACVO_LOCAL};
uint32_t etsi_l[] = {WNI_CFG_EDCA_ETSI_ACBE_LOCAL,
WNI_CFG_EDCA_ETSI_ACBK_LOCAL,
WNI_CFG_EDCA_ETSI_ACVI_LOCAL,
WNI_CFG_EDCA_ETSI_ACVO_LOCAL};
uint32_t ani_b[] = { WNI_CFG_EDCA_ANI_ACBE, WNI_CFG_EDCA_ANI_ACBK,
WNI_CFG_EDCA_ANI_ACVI, WNI_CFG_EDCA_ANI_ACVO};
uint32_t wme_b[] = { WNI_CFG_EDCA_WME_ACBE, WNI_CFG_EDCA_WME_ACBK,
WNI_CFG_EDCA_WME_ACVI, WNI_CFG_EDCA_WME_ACVO};
uint32_t etsi_b[] = {WNI_CFG_EDCA_ETSI_ACBE, WNI_CFG_EDCA_ETSI_ACBK,
WNI_CFG_EDCA_ETSI_ACVI, WNI_CFG_EDCA_ETSI_ACVO};
if (wlan_cfg_get_str(pMac, WNI_CFG_COUNTRY_CODE, country_code_str,
&country_code_len) == QDF_STATUS_SUCCESS &&
cds_is_etsi_europe_country(country_code_str)) {
val = WNI_CFG_EDCA_PROFILE_ETSI_EUROPE;
pe_debug("switch to ETSI EUROPE profile country code %c%c",
country_code_str[0], country_code_str[1]);
} else if (wlan_cfg_get_int(pMac, WNI_CFG_EDCA_PROFILE, &val) !=
QDF_STATUS_SUCCESS) {
pe_err("failed to cfg get EDCA_PROFILE id %d",
WNI_CFG_EDCA_PROFILE);
return QDF_STATUS_E_FAILURE;
}
if (val >= WNI_CFG_EDCA_PROFILE_MAX) {
pe_warn("Invalid EDCA_PROFILE %d, using %d instead", val,
WNI_CFG_EDCA_PROFILE_ANI);
val = WNI_CFG_EDCA_PROFILE_ANI;
}
pe_debug("EdcaProfile: Using %d (%s)", val,
((val == WNI_CFG_EDCA_PROFILE_WMM) ? "WMM" : "HiPerf"));
if (local) {
switch (val) {
case WNI_CFG_EDCA_PROFILE_WMM:
prf = &wme_l[0];
break;
case WNI_CFG_EDCA_PROFILE_ETSI_EUROPE:
prf = &etsi_l[0];
break;
case WNI_CFG_EDCA_PROFILE_ANI:
default:
prf = &ani_l[0];
break;
}
} else {
switch (val) {
case WNI_CFG_EDCA_PROFILE_WMM:
prf = &wme_b[0];
break;
case WNI_CFG_EDCA_PROFILE_ETSI_EUROPE:
prf = &etsi_b[0];
break;
case WNI_CFG_EDCA_PROFILE_ANI:
default:
prf = &ani_b[0];
break;
}
}
for (i = 0; i < 4; i++) {
uint8_t data[WNI_CFG_EDCA_ANI_ACBK_LEN];
uint32_t len = WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN;
if (wlan_cfg_get_str
(pMac, (uint16_t) prf[i], (uint8_t *) &data[0],
&len) != QDF_STATUS_SUCCESS) {
pe_err("cfgGet failed for %d", prf[i]);
return QDF_STATUS_E_FAILURE;
}
if (len > WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN) {
pe_err("cfgGet for %d: length is %d instead of %d",
prf[i], len, WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN);
return QDF_STATUS_E_FAILURE;
}
for (idx = 0; idx < len; idx++)
params[i][idx] = (uint32_t) data[idx];
}
pe_debug("GetParams: local=%d, profile = %d Done", local, val);
return QDF_STATUS_SUCCESS;
}
/**
* broadcast_wmm_of_concurrent_sta_session() - broadcasts wmm info
* @mac_ctx: mac global context
* @session: pesession entry
*
* Return: true if wmm param updated, false if wmm param not updated
*/
static bool
broadcast_wmm_of_concurrent_sta_session(tpAniSirGlobal mac_ctx,
tpPESession session)
{
uint8_t i, j;
tpPESession concurrent_session = NULL;
for (i = 0; i < mac_ctx->lim.maxBssId; i++) {
/*
* Find another INFRA STA AP session on same operating channel.
* The session entry passed to this API is for GO/SoftAP session
* that is getting added currently
*/
if (!((mac_ctx->lim.gpSession[i].valid == true) &&
(mac_ctx->lim.gpSession[i].peSessionId !=
session->peSessionId)
&& (mac_ctx->lim.gpSession[i].currentOperChannel ==
session->currentOperChannel)
&& (mac_ctx->lim.gpSession[i].limSystemRole
== eLIM_STA_ROLE)))
continue;
concurrent_session = &(mac_ctx->lim.gpSession[i]);
break;
}
if (concurrent_session == NULL)
return false;
if (!qdf_mem_cmp(session->gLimEdcaParamsBC,
concurrent_session->gLimEdcaParams,
sizeof(concurrent_session->gLimEdcaParams)))
return false;
/*
* Once atleast one concurrent session on same channel is found and WMM
* broadcast params for current SoftAP/GO session updated, return
*/
for (j = 0; j < MAX_NUM_AC; j++) {
session->gLimEdcaParamsBC[j].aci.acm =
concurrent_session->gLimEdcaParams[j].aci.acm;
session->gLimEdcaParamsBC[j].aci.aifsn =
concurrent_session->gLimEdcaParams[j].aci.aifsn;
session->gLimEdcaParamsBC[j].cw.min =
concurrent_session->gLimEdcaParams[j].cw.min;
session->gLimEdcaParamsBC[j].cw.max =
concurrent_session->gLimEdcaParams[j].cw.max;
session->gLimEdcaParamsBC[j].txoplimit =
concurrent_session->gLimEdcaParams[j].txoplimit;
pe_debug("QoSUpdateBCast changed again due to concurrent INFRA STA session: AC :%d: AIFSN: %d, ACM %d, CWmin %d, CWmax %d, TxOp %d",
j, session->gLimEdcaParamsBC[j].aci.aifsn,
session->gLimEdcaParamsBC[j].aci.acm,
session->gLimEdcaParamsBC[j].cw.min,
session->gLimEdcaParamsBC[j].cw.max,
session->gLimEdcaParamsBC[j].txoplimit);
}
return true;
}
void sch_qos_update_broadcast(tpAniSirGlobal pMac, tpPESession psessionEntry)
{
uint32_t params[4][WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN];
uint32_t cwminidx, cwmaxidx, txopidx;
uint32_t phyMode;
uint8_t i;
bool updated = false;
QDF_STATUS status;
if (sch_get_params(pMac, params, false) != QDF_STATUS_SUCCESS) {
pe_debug("QosUpdateBroadcast: failed");
return;
}
lim_get_phy_mode(pMac, &phyMode, psessionEntry);
pe_debug("QosUpdBcast: mode %d", phyMode);
if (phyMode == WNI_CFG_PHY_MODE_11G) {
cwminidx = WNI_CFG_EDCA_PROFILE_CWMING_IDX;
cwmaxidx = WNI_CFG_EDCA_PROFILE_CWMAXG_IDX;
txopidx = WNI_CFG_EDCA_PROFILE_TXOPG_IDX;
} else if (phyMode == WNI_CFG_PHY_MODE_11B) {
cwminidx = WNI_CFG_EDCA_PROFILE_CWMINB_IDX;
cwmaxidx = WNI_CFG_EDCA_PROFILE_CWMAXB_IDX;
txopidx = WNI_CFG_EDCA_PROFILE_TXOPB_IDX;
} else {
/* This can happen if mode is not set yet, assume 11a mode */
cwminidx = WNI_CFG_EDCA_PROFILE_CWMINA_IDX;
cwmaxidx = WNI_CFG_EDCA_PROFILE_CWMAXA_IDX;
txopidx = WNI_CFG_EDCA_PROFILE_TXOPA_IDX;
}
for (i = 0; i < MAX_NUM_AC; i++) {
if (psessionEntry->gLimEdcaParamsBC[i].aci.acm !=
(uint8_t) params[i][WNI_CFG_EDCA_PROFILE_ACM_IDX]) {
psessionEntry->gLimEdcaParamsBC[i].aci.acm =
(uint8_t) params[i][WNI_CFG_EDCA_PROFILE_ACM_IDX];
updated = true;
}
if (psessionEntry->gLimEdcaParamsBC[i].aci.aifsn !=
(uint8_t) params[i][WNI_CFG_EDCA_PROFILE_AIFSN_IDX]) {
psessionEntry->gLimEdcaParamsBC[i].aci.aifsn =
(uint8_t) params[i][WNI_CFG_EDCA_PROFILE_AIFSN_IDX];
updated = true;
}
if (psessionEntry->gLimEdcaParamsBC[i].cw.min !=
convert_cw(GET_CW(&params[i][cwminidx]))) {
psessionEntry->gLimEdcaParamsBC[i].cw.min =
convert_cw(GET_CW(&params[i][cwminidx]));
updated = true;
}
if (psessionEntry->gLimEdcaParamsBC[i].cw.max !=
convert_cw(GET_CW(&params[i][cwmaxidx]))) {
psessionEntry->gLimEdcaParamsBC[i].cw.max =
convert_cw(GET_CW(&params[i][cwmaxidx]));
updated = true;
}
if (psessionEntry->gLimEdcaParamsBC[i].txoplimit !=
(uint16_t) params[i][txopidx]) {
psessionEntry->gLimEdcaParamsBC[i].txoplimit =
(uint16_t) params[i][txopidx];
updated = true;
}
pe_debug("QoSUpdateBCast: AC :%d: AIFSN: %d, ACM %d, CWmin %d, CWmax %d, TxOp %d",
i, psessionEntry->gLimEdcaParamsBC[i].aci.aifsn,
psessionEntry->gLimEdcaParamsBC[i].aci.acm,
psessionEntry->gLimEdcaParamsBC[i].cw.min,
psessionEntry->gLimEdcaParamsBC[i].cw.max,
psessionEntry->gLimEdcaParamsBC[i].txoplimit);
}
/*
* If there exists a concurrent STA-AP session, use its WMM
* params to broadcast in beacons. WFA Wifi Direct test plan
* 6.1.14 requirement
*/
if (broadcast_wmm_of_concurrent_sta_session(pMac, psessionEntry))
updated = true;
if (updated)
psessionEntry->gLimEdcaParamSetCount++;
status = sch_set_fixed_beacon_fields(pMac, psessionEntry);
if (QDF_IS_STATUS_ERROR(status))
pe_err("Unable to set beacon fields!");
}
void sch_qos_update_local(tpAniSirGlobal pMac, tpPESession psessionEntry)
{
uint32_t params[4][WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN];
QDF_STATUS status;
status = sch_get_params(pMac, params, true /*local */);
if (QDF_IS_STATUS_ERROR(status)) {
pe_err("sch_get_params(local) failed");
return;
}
set_sch_edca_params(pMac, params, psessionEntry);
/* For AP, the bssID is stored in LIM Global context. */
lim_send_edca_params(pMac, psessionEntry->gLimEdcaParams,
psessionEntry->bssIdx, false);
}
/** ----------------------------------------------------------
\fn sch_set_default_edca_params
\brief This function sets the gLimEdcaParams to the default
\ local wmm profile.
\param tpAniSirGlobal pMac
\return none
\ ------------------------------------------------------------ */
void sch_set_default_edca_params(tpAniSirGlobal pMac, tpPESession psessionEntry)
{
uint32_t params[4][WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN];
if (get_wmm_local_params(pMac, params) != QDF_STATUS_SUCCESS) {
pe_err("get_wmm_local_params() failed");
return;
}
set_sch_edca_params(pMac, params, psessionEntry);
return;
}
/** ----------------------------------------------------------
\fn set_sch_edca_params
\brief This function fills in the gLimEdcaParams structure
\ with the given edca params.
\param tpAniSirGlobal pMac
\return none
\ ------------------------------------------------------------ */
static void
set_sch_edca_params(tpAniSirGlobal pMac,
uint32_t params[][WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN],
tpPESession psessionEntry)
{
uint32_t i;
uint32_t cwminidx, cwmaxidx, txopidx;
uint32_t phyMode;
lim_get_phy_mode(pMac, &phyMode, psessionEntry);
pe_debug("lim_get_phy_mode() = %d", phyMode);
/* if (pMac->lim.gLimPhyMode == WNI_CFG_PHY_MODE_11G) */
if (phyMode == WNI_CFG_PHY_MODE_11G) {
cwminidx = WNI_CFG_EDCA_PROFILE_CWMING_IDX;
cwmaxidx = WNI_CFG_EDCA_PROFILE_CWMAXG_IDX;
txopidx = WNI_CFG_EDCA_PROFILE_TXOPG_IDX;
}
/* else if (pMac->lim.gLimPhyMode == WNI_CFG_PHY_MODE_11B) */
else if (phyMode == WNI_CFG_PHY_MODE_11B) {
cwminidx = WNI_CFG_EDCA_PROFILE_CWMINB_IDX;
cwmaxidx = WNI_CFG_EDCA_PROFILE_CWMAXB_IDX;
txopidx = WNI_CFG_EDCA_PROFILE_TXOPB_IDX;
} else {
/* This can happen if mode is not set yet, assume 11a mode */
cwminidx = WNI_CFG_EDCA_PROFILE_CWMINA_IDX;
cwmaxidx = WNI_CFG_EDCA_PROFILE_CWMAXA_IDX;
txopidx = WNI_CFG_EDCA_PROFILE_TXOPA_IDX;
}
for (i = 0; i < MAX_NUM_AC; i++) {
psessionEntry->gLimEdcaParams[i].aci.acm =
(uint8_t) params[i][WNI_CFG_EDCA_PROFILE_ACM_IDX];
psessionEntry->gLimEdcaParams[i].aci.aifsn =
(uint8_t) params[i][WNI_CFG_EDCA_PROFILE_AIFSN_IDX];
psessionEntry->gLimEdcaParams[i].cw.min =
convert_cw(GET_CW(&params[i][cwminidx]));
psessionEntry->gLimEdcaParams[i].cw.max =
convert_cw(GET_CW(&params[i][cwmaxidx]));
psessionEntry->gLimEdcaParams[i].txoplimit =
(uint16_t) params[i][txopidx];
pe_debug("AC :%d: AIFSN: %d, ACM %d, CWmin %d, CWmax %d, TxOp %d",
i, psessionEntry->gLimEdcaParams[i].aci.aifsn,
psessionEntry->gLimEdcaParams[i].aci.acm,
psessionEntry->gLimEdcaParams[i].cw.min,
psessionEntry->gLimEdcaParams[i].cw.max,
psessionEntry->gLimEdcaParams[i].txoplimit);
}
return;
}
/** ----------------------------------------------------------
\fn get_wmm_local_params
\brief This function gets the WMM local edca parameters.
\param tpAniSirGlobal pMac
\param uint32_t params[][WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN]
\return none
\ ------------------------------------------------------------ */
static QDF_STATUS
get_wmm_local_params(tpAniSirGlobal pMac,
uint32_t params[][WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN])
{
uint32_t i, idx;
uint32_t *prf;
uint32_t wme_l[] = {
WNI_CFG_EDCA_WME_ACBE_LOCAL, WNI_CFG_EDCA_WME_ACBK_LOCAL,
WNI_CFG_EDCA_WME_ACVI_LOCAL, WNI_CFG_EDCA_WME_ACVO_LOCAL};
prf = &wme_l[0];
for (i = 0; i < 4; i++) {
uint8_t data[WNI_CFG_EDCA_ANI_ACBK_LEN];
uint32_t len = WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN;
if (wlan_cfg_get_str
(pMac, (uint16_t) prf[i], (uint8_t *) &data[0],
&len) != QDF_STATUS_SUCCESS) {
pe_err("cfgGet failed for %d", prf[i]);
return QDF_STATUS_E_FAILURE;
}
if (len > WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN) {
pe_err("cfgGet for %d: length is %d instead of %d",
prf[i], len, WNI_CFG_EDCA_ANI_ACBK_LOCAL_LEN);
return QDF_STATUS_E_FAILURE;
}
for (idx = 0; idx < len; idx++)
params[i][idx] = (uint32_t) data[idx];
}
return QDF_STATUS_SUCCESS;
}
/** ----------------------------------------------------------
\fn sch_edca_profile_update
\brief This function updates the local and broadcast
\ EDCA params in the gLimEdcaParams structure. It also
\ updates the edcaParamSetCount.
\param tpAniSirGlobal pMac
\return none
\ ------------------------------------------------------------ */
void sch_edca_profile_update(tpAniSirGlobal pMac, tpPESession psessionEntry)
{
if (LIM_IS_AP_ROLE(psessionEntry) ||
LIM_IS_IBSS_ROLE(psessionEntry)) {
sch_qos_update_local(pMac, psessionEntry);
sch_qos_update_broadcast(pMac, psessionEntry);
}
}
/* -------------------------------------------------------------------- */