blob: afcf20362de17d996445d6c10e9440716fe21aa2 [file] [log] [blame]
/*
* Copyright (c) 2012-2013, 2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/*
* This file contains TSPEC and STA admit control related functions
* NOTE: applies only to AP builds
*
* Author: Sandesh Goel
* Date: 02/25/02
* History:-
* Date Modified by Modification Information
* --------------------------------------------------------------------
*
*/
#include "limDebug.h"
#include "sysDef.h"
#include "limApi.h"
#include "cfgApi.h" // wlan_cfgGetInt()
#include "limTrace.h"
#include "limSendSmeRspMessages.h"
#include "limTypes.h"
#define ADMIT_CONTROL_LOGLEVEL LOG1
#define ADMIT_CONTROL_POLICY_LOGLEVEL LOG1
#define ADMIT_CONTROL_MIN_INTERVAL 1000 // min acceptable service interval 1mSec
/* total available bandwidth in bps in each phy mode
* these should be defined in hal or dph - replace these later
*/
#define LIM_TOTAL_BW_11A 54000000
#define LIM_MIN_BW_11A 6000000
#define LIM_TOTAL_BW_11B 11000000
#define LIM_MIN_BW_11B 1000000
#define LIM_TOTAL_BW_11G LIM_TOTAL_BW_11A
#define LIM_MIN_BW_11G LIM_MIN_BW_11B
// conversion factors
#define LIM_CONVERT_SIZE_BITS(numBytes) ((numBytes) * 8)
#define LIM_CONVERT_RATE_MBPS(rate) ((rate)/1000000)
/* ANI sta's support enhanced rates, so the effective medium time used is
* half that of other stations. This is the same as if they were requesting
* half the badnwidth - so we adjust ANI sta's accordingly for bandwidth
* calculations. Also enhanced rates apply only in case of non 11B mode.
*/
#define LIM_STA_BW_ADJUST(aniPeer, phyMode, bw) \
(((aniPeer) && ((phyMode) != WNI_CFG_PHY_MODE_11B)) \
? ((bw)/2) : (bw))
//------------------------------------------------------------------------------
// local protos
static tSirRetStatus
limCalculateSvcInt(tpAniSirGlobal, tSirMacTspecIE *, tANI_U32 *);
#if 0 //only EDCA is supported now
static tSirRetStatus
limValidateTspecHcca(tpAniSirGlobal, tSirMacTspecIE *);
#endif
static tSirRetStatus
limValidateTspecEdca(tpAniSirGlobal, tSirMacTspecIE *, tpPESession);
static tSirRetStatus
limValidateTspec(tpAniSirGlobal, tSirMacTspecIE *, tpPESession);
static void
limComputeMeanBwUsed(tpAniSirGlobal, tANI_U32 *, tANI_U32, tpLimTspecInfo, tpPESession);
static void
limGetAvailableBw(tpAniSirGlobal, tANI_U32 *, tANI_U32 *, tANI_U32, tANI_U32);
static tSirRetStatus
limAdmitPolicyOversubscription(tpAniSirGlobal, tSirMacTspecIE *, tpLimAdmitPolicyInfo, tpLimTspecInfo, tpPESession);
static tSirRetStatus
limTspecFindByStaAddr(tpAniSirGlobal, tANI_U8 *, tSirMacTspecIE*, tpLimTspecInfo, tpLimTspecInfo *);
static tSirRetStatus
limValidateAccessPolicy(tpAniSirGlobal, tANI_U8, tANI_U16, tpPESession);
/** -------------------------------------------------------------
\fn limCalculateSvcInt
\brief TSPEC validation and servcie interval determination
\param tpAniSirGlobal pMac
\param tSirMacTspecIE *pTspec
\param tANI_U32 *pSvcInt
\return eSirRetStatus - status of the comparison
-------------------------------------------------------------*/
static tSirRetStatus
limCalculateSvcInt(
tpAniSirGlobal pMac,
tSirMacTspecIE *pTspec,
tANI_U32 *pSvcInt)
{
tANI_U32 msduSz, dataRate;
*pSvcInt = 0;
// if a service interval is already specified, we are done
if ((pTspec->minSvcInterval != 0) || (pTspec->maxSvcInterval != 0))
{
*pSvcInt = (pTspec->maxSvcInterval != 0)
? pTspec->maxSvcInterval : pTspec->minSvcInterval;
return eSIR_SUCCESS;
}
/* Masking off the fixed bits according to definition of MSDU size
* in IEEE 802.11-2007 spec (section 7.3.2.30). Nominal MSDU size
* is defined as: Bit[0:14]=Size, Bit[15]=Fixed
*/
if (pTspec->nomMsduSz != 0)
msduSz = (pTspec->nomMsduSz & 0x7fff);
else if (pTspec->maxMsduSz != 0)
msduSz = pTspec->maxMsduSz;
else
{
PELOGE(limLog(pMac, LOGE, FL("MsduSize not specified"));)
return eSIR_FAILURE;
}
/* need to calculate a reasonable service interval
* this is simply the msduSz/meanDataRate
*/
if (pTspec->meanDataRate != 0) dataRate = pTspec->meanDataRate;
else if (pTspec->peakDataRate != 0) dataRate = pTspec->peakDataRate;
else if (pTspec->minDataRate != 0) dataRate = pTspec->minDataRate;
else
{
PELOGE(limLog(pMac, LOGE, FL("DataRate not specified"));)
return eSIR_FAILURE;
}
*pSvcInt = LIM_CONVERT_SIZE_BITS(msduSz) / LIM_CONVERT_RATE_MBPS(dataRate);
return eSIR_FAILURE;
}
#if 0 //only EDCA is supported now
/** -------------------------------------------------------------
\fn limValidateTspecHcca
\brief validate the parameters in the hcca tspec
mandatory fields are derived from 11e Annex I (Table I.1)
\param tpAniSirGlobal pMac
\param tSirMacTspecIE *pTspec
\return eSirRetStatus - status
-------------------------------------------------------------*/
static tSirRetStatus
limValidateTspecHcca(
tpAniSirGlobal pMac,
tSirMacTspecIE *pTspec)
{
tANI_U32 maxPhyRate, minPhyRate;
tANI_U32 phyMode;
tSirRetStatus retval = eSIR_SUCCESS;
/* make sure a TSID is being requested */
if (pTspec->tsinfo.traffic.tsid < SIR_MAC_HCCA_TSID_MIN)
{
limLog(pMac, LOGW, FL("tsid %d must be >%d)"),
pTspec->tsinfo.traffic.tsid, SIR_MAC_HCCA_TSID_MIN);
retval = eSIR_FAILURE;
}
/*
* With Polaris, there is a limitation in that the tsid cannot be arbitary
* but is based on the qid. Thus, we cannot have a tspec which requests
* a tsid of 13 and userPrio of 7, the bottom three bits of the tsid must
* correspond to the userPrio
*/
if (pTspec->tsinfo.traffic.userPrio !=
(pTspec->tsinfo.traffic.tsid - SIR_MAC_HCCA_TSID_MIN))
{
limLog(pMac, LOGE, FL("TSid=0x%x, userPrio=%d: is not allowed"),
pTspec->tsinfo.traffic.tsid, pTspec->tsinfo.traffic.userPrio);
retval = eSIR_FAILURE;
}
// an inactivity interval is mandatory
if (pTspec->inactInterval == 0)
{
PELOGW(limLog(pMac, LOGW, FL("inactInterval unspecified!"));)
retval = eSIR_FAILURE;
}
// surplus BW must be specified if a delay Bound is specified
if ((pTspec->delayBound != 0) && (pTspec->surplusBw == 0))
{
limLog(pMac, LOGW, FL("delayBound %d, but surplusBw unspecified!"),
pTspec->delayBound);
retval = eSIR_FAILURE;
}
// minPhyRate must always be specified and cannot exceed maximum supported
limGetPhyMode(pMac, &phyMode);
//limGetAvailableBw(pMac, &maxPhyRate, &minPhyRate, pMac->dph.gDphPhyMode,
// 1 /* bandwidth mult factor */);
limGetAvailableBw(pMac, &maxPhyRate, &minPhyRate, phyMode,
1 /* bandwidth mult factor */);
if ((pTspec->minPhyRate == 0)
|| (pTspec->minPhyRate > maxPhyRate)
|| (pTspec->minPhyRate < minPhyRate))
{
limLog(pMac, LOGW, FL("minPhyRate (%d) invalid"),
pTspec->minPhyRate);
retval = eSIR_FAILURE;
}
/* NOTE: we will require all Tspec's to specify a mean data rate (and so
* also the min and peak data rates)
*/
if ((pTspec->minDataRate == 0) ||
(pTspec->meanDataRate == 0) ||
(pTspec->peakDataRate == 0))
{
limLog(pMac, LOGW, FL("DataRate must be specified (min %d, mean %d, peak %d)"),
pTspec->minDataRate, pTspec->meanDataRate, pTspec->peakDataRate);
retval = eSIR_FAILURE;
}
// mean data rate can't be more than the min phy rate
if (pTspec->meanDataRate > pTspec->minPhyRate)
{
limLog(pMac, LOGW, FL("Data rate (%d) is more than Phyrate %d"),
pTspec->meanDataRate, pTspec->minPhyRate);
return eSIR_FAILURE;
}
/* if the tspec specifies a service interval, we won't accept tspec's
* with service interval less than our allowed minimum, also either both
* min and max must be specified or neither should be specified (in which
* case, HC determines the appropriate service interval
*/
if ((pTspec->minSvcInterval != 0) || (pTspec->maxSvcInterval != 0))
{
// max < min is ridiculous
if (pTspec->maxSvcInterval < pTspec->minSvcInterval)
{
limLog(pMac, LOGW, FL("maxSvcInt %d > minSvcInterval %d!!"),
pTspec->maxSvcInterval, pTspec->minSvcInterval);
retval = eSIR_FAILURE;
}
if (pTspec->maxSvcInterval < ADMIT_CONTROL_MIN_INTERVAL)
{
limLog(pMac, LOGW, FL("maxSvcInt %d must be >%d"),
pTspec->maxSvcInterval, ADMIT_CONTROL_MIN_INTERVAL);
retval = eSIR_FAILURE;
}
}
else // min and max both unspecified
{
/* no service interval is specified, so make sure the parameters
* needed to determine one are specified in the tspec
* minPhyRate, meanDataRate and nomMsduSz are needed, only nomMsduSz
* must be checked here since the other two are already validated
*/
if (pTspec->nomMsduSz == 0)
{
PELOGW(limLog(pMac, LOGW, FL("No svcInt and no MsduSize specified"));)
retval = eSIR_FAILURE;
}
}
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("return status %d"), retval);
return retval;
}
#endif //only edca is supported now.
/** -------------------------------------------------------------
\fn limValidateTspecEdca
\brief validate the parameters in the edca tspec
mandatory fields are derived from 11e Annex I (Table I.1)
\param tpAniSirGlobal pMac
\param tSirMacTspecIE *pTspec
\return eSirRetStatus - status
-------------------------------------------------------------*/
static tSirRetStatus
limValidateTspecEdca(
tpAniSirGlobal pMac,
tSirMacTspecIE *pTspec,
tpPESession psessionEntry)
{
tANI_U32 maxPhyRate, minPhyRate;
tANI_U32 phyMode;
tSirRetStatus retval = eSIR_SUCCESS;
limGetPhyMode(pMac, &phyMode, psessionEntry);
//limGetAvailableBw(pMac, &maxPhyRate, &minPhyRate, pMac->dph.gDphPhyMode,
// 1 /* bandwidth mult factor */);
limGetAvailableBw(pMac, &maxPhyRate, &minPhyRate, phyMode,
1 /* bandwidth mult factor */);
// mandatory fields are derived from 11e Annex I (Table I.1)
if ((pTspec->nomMsduSz == 0) ||
(pTspec->meanDataRate == 0) ||
(pTspec->surplusBw == 0) ||
(pTspec->minPhyRate == 0) ||
(pTspec->minPhyRate > maxPhyRate))
{
limLog(pMac, LOGW, FL("Invalid EDCA Tspec: NomMsdu %d, meanDataRate %d, surplusBw %d, minPhyRate %d"),
pTspec->nomMsduSz, pTspec->meanDataRate, pTspec->surplusBw, pTspec->minPhyRate);
retval = eSIR_FAILURE;
}
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("return status %d"), retval);
return retval;
}
/** -------------------------------------------------------------
\fn limValidateTspec
\brief validate the offered tspec
\param tpAniSirGlobal pMac
\param tSirMacTspecIE *pTspec
\return eSirRetStatus - status
-------------------------------------------------------------*/
static tSirRetStatus
limValidateTspec(
tpAniSirGlobal pMac,
tSirMacTspecIE *pTspec,
tpPESession psessionEntry)
{
tSirRetStatus retval = eSIR_SUCCESS;
switch (pTspec->tsinfo.traffic.accessPolicy)
{
case SIR_MAC_ACCESSPOLICY_EDCA:
if ((retval = limValidateTspecEdca(pMac, pTspec, psessionEntry)) != eSIR_SUCCESS)
PELOGW(limLog(pMac, LOGW, FL("EDCA tspec invalid"));)
break;
case SIR_MAC_ACCESSPOLICY_HCCA:
#if 0 //Not supported right now.
if ((retval = limValidateTspecHcca(pMac, pTspec)) != eSIR_SUCCESS)
PELOGW(limLog(pMac, LOGW, FL("HCCA tspec invalid"));)
break;
#endif
case SIR_MAC_ACCESSPOLICY_BOTH:
// TBD: should we support hybrid tspec as well?? for now, just fall through
default:
limLog(pMac, LOGW, FL("AccessType %d not supported"),
pTspec->tsinfo.traffic.accessPolicy);
retval = eSIR_FAILURE;
break;
}
return retval;
}
//-----------------------------------------------------------------------------
// Admit Control Policy
/** -------------------------------------------------------------
\fn limComputeMeanBwUsed
\brief determime the used/allocated bandwidth
\param tpAniSirGlobal pMac
\param tANI_U32 *pBw
\param tANI_U32 phyMode
\param tpLimTspecInfo pTspecInfo
\return eSirRetStatus - status
-------------------------------------------------------------*/
static void
limComputeMeanBwUsed(
tpAniSirGlobal pMac,
tANI_U32 *pBw,
tANI_U32 phyMode,
tpLimTspecInfo pTspecInfo,
tpPESession psessionEntry)
{
tANI_U32 ctspec;
*pBw = 0;
for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecInfo++)
{
if (pTspecInfo->inuse)
{
tpDphHashNode pSta = dphGetHashEntry(pMac, pTspecInfo->assocId, &psessionEntry->dph.dphHashTable);
if (pSta == NULL)
{
// maybe we should delete the tspec??
limLog(pMac, LOGE, FL("Tspec %d (assocId %d): dphNode not found"),
ctspec, pTspecInfo->assocId);
continue;
}
//FIXME: need to take care of taurusPeer, titanPeer, 11npeer too.
*pBw += LIM_STA_BW_ADJUST(pSta->aniPeer, phyMode, pTspecInfo->tspec.meanDataRate);
}
}
}
/** -------------------------------------------------------------
\fn limGetAvailableBw
\brief based on the phy mode and the bw_factor, determine the total bandwidth that
can be supported
\param tpAniSirGlobal pMac
\param tANI_U32 *pMaxBw
\param tANI_U32 *pMinBw
\param tANI_U32 phyMode
\param tANI_U32 bw_factor
\return eSirRetStatus - status
-------------------------------------------------------------*/
static void
limGetAvailableBw(
tpAniSirGlobal pMac,
tANI_U32 *pMaxBw,
tANI_U32 *pMinBw,
tANI_U32 phyMode,
tANI_U32 bw_factor)
{
switch (phyMode)
{
case WNI_CFG_PHY_MODE_11B:
*pMaxBw = LIM_TOTAL_BW_11B;
*pMinBw = LIM_MIN_BW_11B;
break;
case WNI_CFG_PHY_MODE_11A:
*pMaxBw = LIM_TOTAL_BW_11A;
*pMinBw = LIM_MIN_BW_11A;
break;
case WNI_CFG_PHY_MODE_11G:
case WNI_CFG_PHY_MODE_NONE:
default:
*pMaxBw = LIM_TOTAL_BW_11G;
*pMinBw = LIM_MIN_BW_11G;
break;
}
*pMaxBw *= bw_factor;
}
/** -------------------------------------------------------------
\fn limAdmitPolicyOversubscription
\brief simple admission control policy based on oversubscription
if the total bandwidth of all admitted tspec's exceeds (factor * phy-bw) then
reject the tspec, else admit it. The phy-bw is the peak available bw in the
current phy mode. The 'factor' is the configured oversubscription factor.
\param tpAniSirGlobal pMac
\param tSirMacTspecIE *pTspec
\param tpLimAdmitPolicyInfo pAdmitPolicy
\param tpLimTspecInfo pTspecInfo
\return eSirRetStatus - status
-------------------------------------------------------------*/
/*
* simple admission control policy based on oversubscription
* if the total bandwidth of all admitted tspec's exceeds (factor * phy-bw) then
* reject the tspec, else admit it. The phy-bw is the peak available bw in the
* current phy mode. The 'factor' is the configured oversubscription factor.
*/
static tSirRetStatus
limAdmitPolicyOversubscription(
tpAniSirGlobal pMac,
tSirMacTspecIE *pTspec,
tpLimAdmitPolicyInfo pAdmitPolicy,
tpLimTspecInfo pTspecInfo,
tpPESession psessionEntry)
{
tANI_U32 totalbw, minbw, usedbw;
tANI_U32 phyMode;
// determine total bandwidth used so far
limGetPhyMode(pMac, &phyMode, psessionEntry);
//limComputeMeanBwUsed(pMac, &usedbw, pMac->dph.gDphPhyMode, pTspecInfo);
limComputeMeanBwUsed(pMac, &usedbw, phyMode, pTspecInfo, psessionEntry);
// determine how much bandwidth is available based on the current phy mode
//limGetAvailableBw(pMac, &totalbw, &minbw, pMac->dph.gDphPhyMode, pAdmitPolicy->bw_factor);
limGetAvailableBw(pMac, &totalbw, &minbw, phyMode, pAdmitPolicy->bw_factor);
if (usedbw > totalbw) // this can't possibly happen
return eSIR_FAILURE;
if ((totalbw - usedbw) < pTspec->meanDataRate)
{
limLog(pMac, ADMIT_CONTROL_POLICY_LOGLEVEL,
FL("Total BW %d, Used %d, Tspec request %d not possible"),
totalbw, usedbw, pTspec->meanDataRate);
return eSIR_FAILURE;
}
return eSIR_SUCCESS;
}
/** -------------------------------------------------------------
\fn limAdmitPolicy
\brief determine the current admit control policy and apply it for the offered tspec
\param tpAniSirGlobal pMac
\param tSirMacTspecIE *pTspec
\return eSirRetStatus - status
-------------------------------------------------------------*/
tSirRetStatus limAdmitPolicy(
tpAniSirGlobal pMac,
tSirMacTspecIE *pTspec,
tpPESession psessionEntry)
{
tSirRetStatus retval = eSIR_FAILURE;
tpLimAdmitPolicyInfo pAdmitPolicy = &pMac->lim.admitPolicyInfo;
switch (pAdmitPolicy->type)
{
case WNI_CFG_ADMIT_POLICY_ADMIT_ALL:
retval = eSIR_SUCCESS;
break;
case WNI_CFG_ADMIT_POLICY_BW_FACTOR:
retval = limAdmitPolicyOversubscription(pMac, pTspec,
&pMac->lim.admitPolicyInfo, &pMac->lim.tspecInfo[0], psessionEntry);
if (retval != eSIR_SUCCESS)
PELOGE(limLog(pMac, LOGE, FL("rejected by BWFactor policy"));)
break;
case WNI_CFG_ADMIT_POLICY_REJECT_ALL:
retval = eSIR_FAILURE;
break;
default:
retval = eSIR_SUCCESS;
limLog(pMac, LOGE, FL("Admit Policy %d unknown, admitting all traffic"),
pAdmitPolicy->type);
break;
}
return retval;
}
/** -------------------------------------------------------------
\fn limTspecDelete
\brief delete the specified tspec
\param tpAniSirGlobal pMac
\param tpLimTspecInfo pInfo
\return eSirRetStatus - status
-------------------------------------------------------------*/
//-----------------------------------------------------------------------------
// delete the specified tspec
void limTspecDelete(tpAniSirGlobal pMac, tpLimTspecInfo pInfo)
{
if (pInfo == NULL)
return;
//pierre
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("tspec entry = %d"), pInfo->idx);
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("delete tspec %pK"), pInfo);
pInfo->inuse = 0;
// clear the hcca/parameterized queue indicator
#if 0
if ((pInfo->tspec.tsinfo.traffic.direction == SIR_MAC_DIRECTION_UPLINK) ||
(pInfo->tspec.tsinfo.traffic.direction == SIR_MAC_DIRECTION_BIDIR))
queue[pInfo->staid][pInfo->tspec.tsinfo.traffic.userPrio][SCH_UL_QUEUE].ts = 0;
#endif
return;
}
/** -------------------------------------------------------------
\fn limTspecFindByStaAddr
\brief Send halMsg_AddTs to HAL
\param tpAniSirGlobal pMac
\param \param tANI_U8 *pAddr
\param tSirMacTspecIE *pTspecIE
\param tpLimTspecInfo pTspecList
\param tpLimTspecInfo *ppInfo
\return eSirRetStatus - status
-------------------------------------------------------------*/
// find the specified tspec in the list
static tSirRetStatus
limTspecFindByStaAddr(
tpAniSirGlobal pMac,
tANI_U8 *pAddr,
tSirMacTspecIE *pTspecIE,
tpLimTspecInfo pTspecList,
tpLimTspecInfo *ppInfo)
{
int ctspec;
*ppInfo = NULL;
for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecList++)
{
if ((pTspecList->inuse)
&& (vos_mem_compare(pAddr, pTspecList->staAddr, sizeof(pTspecList->staAddr)))
&& (vos_mem_compare((tANI_U8 *) pTspecIE, (tANI_U8 *) &pTspecList->tspec,
sizeof(tSirMacTspecIE))))
{
*ppInfo = pTspecList;
return eSIR_SUCCESS;
}
}
return eSIR_FAILURE;
}
/** -------------------------------------------------------------
\fn limTspecFindByAssocId
\brief find tspec with matchin staid and Tspec
\param tpAniSirGlobal pMac
\param tANI_U32 staid
\param tSirMacTspecIE *pTspecIE
\param tpLimTspecInfo pTspecList
\param tpLimTspecInfo *ppInfo
\return eSirRetStatus - status
-------------------------------------------------------------*/
tSirRetStatus
limTspecFindByAssocId(
tpAniSirGlobal pMac,
tANI_U16 assocId,
tSirMacTspecIE *pTspecIE,
tpLimTspecInfo pTspecList,
tpLimTspecInfo *ppInfo)
{
int ctspec;
*ppInfo = NULL;
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("Trying to find tspec entry for assocId = %d"), assocId);
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("pTsInfo->traffic.direction = %d, pTsInfo->traffic.tsid = %d"),
pTspecIE->tsinfo.traffic.direction, pTspecIE->tsinfo.traffic.tsid);
for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecList++)
{
if ((pTspecList->inuse)
&& (assocId == pTspecList->assocId)
&& (vos_mem_compare((tANI_U8 *)pTspecIE, (tANI_U8 *)&pTspecList->tspec,
sizeof(tSirMacTspecIE))))
{
*ppInfo = pTspecList;
return eSIR_SUCCESS;
}
}
return eSIR_FAILURE;
}
/** -------------------------------------------------------------
\fn limFindTspec
\brief finding a TSPEC entry with assocId, tsinfo.direction and tsinfo.tsid
\param tANI_U16 assocId
\param tpAniSirGlobal pMac
\param tSirMacTSInfo *pTsInfo
\param tpLimTspecInfo pTspecList
\param tpLimTspecInfo *ppInfo
\return eSirRetStatus - status of the comparison
-------------------------------------------------------------*/
tSirRetStatus
limFindTspec(
tpAniSirGlobal pMac,
tANI_U16 assocId,
tSirMacTSInfo *pTsInfo,
tpLimTspecInfo pTspecList,
tpLimTspecInfo *ppInfo)
{
int ctspec;
*ppInfo = NULL;
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("Trying to find tspec entry for assocId = %d"), assocId);
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("pTsInfo->traffic.direction = %d, pTsInfo->traffic.tsid = %d"),
pTsInfo->traffic.direction, pTsInfo->traffic.tsid);
for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecList++)
{
if ((pTspecList->inuse)
&& (assocId == pTspecList->assocId)
&& (pTsInfo->traffic.direction == pTspecList->tspec.tsinfo.traffic.direction)
&& (pTsInfo->traffic.tsid == pTspecList->tspec.tsinfo.traffic.tsid))
{
*ppInfo = pTspecList;
return eSIR_SUCCESS;
}
}
return eSIR_FAILURE;
}
/** -------------------------------------------------------------
\fn limTspecAdd
\brief add or update the specified tspec to the tspec list
\param tpAniSirGlobal pMac
\param tANI_U8 *pAddr
\param tANI_U16 assocId
\param tSirMacTspecIE *pTspec
\param tANI_U32 interval
\param tpLimTspecInfo *ppInfo
\return eSirRetStatus - status of the comparison
-------------------------------------------------------------*/
tSirRetStatus limTspecAdd(
tpAniSirGlobal pMac,
tANI_U8 *pAddr,
tANI_U16 assocId,
tSirMacTspecIE *pTspec,
tANI_U32 interval,
tpLimTspecInfo *ppInfo)
{
tpLimTspecInfo pTspecList = &pMac->lim.tspecInfo[0];
*ppInfo = NULL;
// validate the assocId
if (assocId >= pMac->lim.maxStation)
{
PELOGE(limLog(pMac, LOGE, FL("Invalid assocId 0x%x"), assocId);)
return eSIR_FAILURE;
}
//decide whether to add/update
{
*ppInfo = NULL;
if(eSIR_SUCCESS == limFindTspec(pMac, assocId, &pTspec->tsinfo, pTspecList, ppInfo))
{
//update this entry.
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("updating TSPEC table entry = %d"),
(*ppInfo)->idx);
}
else
{
/* We didn't find one to update. So find a free slot in the
* LIM TSPEC list and add this new entry
*/
tANI_U8 ctspec = 0;
for (ctspec = 0 , pTspecList = &pMac->lim.tspecInfo[0]; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecList++)
{
if (! pTspecList->inuse)
{
limLog(pMac, LOG1, FL("Found free slot in TSPEC list. Add to TSPEC table entry %d"), ctspec);
break;
}
}
if (ctspec >= LIM_NUM_TSPEC_MAX)
return eSIR_FAILURE;
//Record the new index entry
pTspecList->idx = ctspec;
}
}
// update the tspec info
pTspecList->tspec = *pTspec;
pTspecList->assocId = assocId;
vos_mem_copy(pTspecList->staAddr, pAddr, sizeof(pTspecList->staAddr));
// for edca tspec's, we are all done
if (pTspec->tsinfo.traffic.accessPolicy == SIR_MAC_ACCESSPOLICY_EDCA)
{
pTspecList->inuse = 1;
*ppInfo = pTspecList;
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("added entry for EDCA AccessPolicy"));
return eSIR_SUCCESS;
}
/*
* for hcca tspec's, must set the parameterized bit in the queues
* the 'ts' bit in the queue data structure indicates that the queue is
* parameterized (hcca). When the schedule is written this bit is used
* in the tsid field (bit 3) and the other three bits (0-2) are simply
* filled in as the user priority (or qid). This applies only to uplink
* polls where the qos control field must contain the tsid specified in the
* tspec.
*/
#if 0
if ((pTspec->tsinfo.traffic.direction == SIR_MAC_DIRECTION_UPLINK) ||
(pTspec->tsinfo.traffic.direction == SIR_MAC_DIRECTION_BIDIR))
queue[staid][pTspec->tsinfo.traffic.userPrio][SCH_UL_QUEUE].ts = 1;
#endif
pTspecList->inuse = 1;
*ppInfo = pTspecList;
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("added entry for HCCA AccessPolicy"));
return eSIR_SUCCESS;
}
/** -------------------------------------------------------------
\fn limValidateAccessPolicy
\brief Validates Access policy
\param tpAniSirGlobal pMac
\param tANI_U8 accessPolicy
\param tANI_U16 assocId
\return eSirRetStatus - status
-------------------------------------------------------------*/
static tSirRetStatus
limValidateAccessPolicy(
tpAniSirGlobal pMac,
tANI_U8 accessPolicy,
tANI_U16 assocId,
tpPESession psessionEntry)
{
tSirRetStatus retval = eSIR_FAILURE;
tpDphHashNode pSta = dphGetHashEntry(pMac, assocId, &psessionEntry->dph.dphHashTable);
if ((pSta == NULL) || (! pSta->valid))
{
PELOGE(limLog(pMac, LOGE, FL("invalid station address passed"));)
return eSIR_FAILURE;
}
switch (accessPolicy)
{
case SIR_MAC_ACCESSPOLICY_EDCA:
if (pSta->wmeEnabled || pSta->lleEnabled)
retval = eSIR_SUCCESS;
break;
case SIR_MAC_ACCESSPOLICY_HCCA:
case SIR_MAC_ACCESSPOLICY_BOTH:
#if 0 //only EDCA supported for now.
// TBD: check wsm doesn't support the hybrid access policy
if (pSta->wsmEnabled || pSta->lleEnabled)
retval = eSIR_SUCCESS;
break;
#endif //only EDCA supported for now.
default:
PELOGE(limLog(pMac, LOGE, FL("Invalid accessPolicy %d"), accessPolicy);)
break;
}
if (retval != eSIR_SUCCESS)
limLog(pMac, LOGW, FL("failed (accPol %d, staId %d, lle %d, wme %d, wsm %d)"),
accessPolicy, pSta->staIndex, pSta->lleEnabled, pSta->wmeEnabled, pSta->wsmEnabled);
return retval;
}
/** -------------------------------------------------------------
\fn limAdmitControlAddTS
\brief Determine if STA with the specified TSPEC can be admitted. If it can,
a schedule element is provided
\param tpAniSirGlobal pMac
\param tANI_U8 *pAddr,
\param tSirAddtsReqInfo *pAddts,
\param tSirMacQosCapabilityIE *pQos,
\param tANI_U16 assocId, // assocId, valid only if alloc==true
\param tANI_U8 alloc, // true=>allocate bw for this tspec,
// else determine only if space is available
\param tSirMacScheduleIE *pSch,
\param tANI_U8 *pTspecIdx //index to the lim tspec table.
\return eSirRetStatus - status
-------------------------------------------------------------*/
tSirRetStatus limAdmitControlAddTS(
tpAniSirGlobal pMac,
tANI_U8 *pAddr,
tSirAddtsReqInfo *pAddts,
tSirMacQosCapabilityStaIE *pQos,
tANI_U16 assocId, // assocId, valid only if alloc==true
tANI_U8 alloc, // true=>allocate bw for this tspec,
// else determine only if space is available
tSirMacScheduleIE *pSch,
tANI_U8 *pTspecIdx, //index to the lim tspec table.
tpPESession psessionEntry
)
{
tpLimTspecInfo pTspecInfo;
tSirRetStatus retval;
tANI_U32 svcInterval;
(void) pQos;
// TBD: modify tspec as needed
// EDCA: need to fill in the medium time and the minimum phy rate
// to be consistent with the desired traffic parameters.
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("tsid %d, directn %d, start %d, intvl %d, accPolicy %d, up %d"),
pAddts->tspec.tsinfo.traffic.tsid, pAddts->tspec.tsinfo.traffic.direction,
pAddts->tspec.svcStartTime, pAddts->tspec.minSvcInterval,
pAddts->tspec.tsinfo.traffic.accessPolicy, pAddts->tspec.tsinfo.traffic.userPrio);
// check for duplicate tspec
retval = (alloc)
? limTspecFindByAssocId(pMac, assocId, &pAddts->tspec, &pMac->lim.tspecInfo[0], &pTspecInfo)
: limTspecFindByStaAddr(pMac, pAddr, &pAddts->tspec, &pMac->lim.tspecInfo[0], &pTspecInfo);
if (retval == eSIR_SUCCESS)
{
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("duplicate tspec (index %d)!"), pTspecInfo->idx);
return eSIR_FAILURE;
}
// check that the tspec's are well formed and acceptable
if (limValidateTspec(pMac, &pAddts->tspec, psessionEntry) != eSIR_SUCCESS)
{
PELOGW(limLog(pMac, LOGW, FL("tspec validation failed"));)
return eSIR_FAILURE;
}
// determine a service interval for the tspec
if (limCalculateSvcInt(pMac, &pAddts->tspec, &svcInterval) != eSIR_SUCCESS)
{
PELOGW(limLog(pMac, LOGW, FL("SvcInt calculate failed"));)
return eSIR_FAILURE;
}
// determine if the tspec can be admitted or not based on current policy
if (limAdmitPolicy(pMac, &pAddts->tspec, psessionEntry) != eSIR_SUCCESS)
{
PELOGW(limLog(pMac, LOGW, FL("tspec rejected by admit control policy"));)
return eSIR_FAILURE;
}
// fill in a schedule if requested
if (pSch != NULL)
{
vos_mem_set((tANI_U8 *) pSch, sizeof(*pSch), 0);
pSch->svcStartTime = pAddts->tspec.svcStartTime;
pSch->svcInterval = svcInterval;
pSch->maxSvcDuration = (tANI_U16) pSch->svcInterval; // use SP = SI
pSch->specInterval = 0x1000; // fixed for now: TBD
pSch->info.direction = pAddts->tspec.tsinfo.traffic.direction;
pSch->info.tsid = pAddts->tspec.tsinfo.traffic.tsid;
pSch->info.aggregation = 0; // no support for aggregation for now: TBD
}
// if no allocation is requested, done
if (! alloc)
return eSIR_SUCCESS;
// check that we are in the proper mode to deal with the tspec type
if (limValidateAccessPolicy(pMac, (tANI_U8) pAddts->tspec.tsinfo.traffic.accessPolicy, assocId, psessionEntry) != eSIR_SUCCESS)
{
limLog(pMac, LOGW, FL("AccessPolicy %d is not valid in current mode"),
pAddts->tspec.tsinfo.traffic.accessPolicy);
return eSIR_FAILURE;
}
// add tspec to list
if (limTspecAdd(pMac, pAddr, assocId, &pAddts->tspec, svcInterval, &pTspecInfo)
!= eSIR_SUCCESS)
{
PELOGE(limLog(pMac, LOGE, FL("no space in tspec list"));)
return eSIR_FAILURE;
}
//passing lim tspec table index to the caller
*pTspecIdx = pTspecInfo->idx;
return eSIR_SUCCESS;
}
/** -------------------------------------------------------------
\fn limAdmitControlDeleteTS
\brief Delete the specified Tspec for the specified STA
\param tpAniSirGlobal pMac
\param tANI_U16 assocId
\param tSirMacTSInfo *pTsInfo
\param tANI_U8 *pTsStatus
\param tANI_U8 *ptspecIdx
\return eSirRetStatus - status
-------------------------------------------------------------*/
tSirRetStatus
limAdmitControlDeleteTS(
tpAniSirGlobal pMac,
tANI_U16 assocId,
tSirMacTSInfo *pTsInfo,
tANI_U8 *pTsStatus,
tANI_U8 *ptspecIdx)
{
tpLimTspecInfo pTspecInfo = NULL;
if (pTsStatus != NULL)
*pTsStatus = 0;
if (limFindTspec(pMac, assocId, pTsInfo, &pMac->lim.tspecInfo[0], &pTspecInfo) == eSIR_SUCCESS)
{
if(pTspecInfo != NULL)
{
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("Tspec entry %d found"), pTspecInfo->idx);
*ptspecIdx = pTspecInfo->idx;
limTspecDelete(pMac, pTspecInfo);
return eSIR_SUCCESS;
}
}
return eSIR_FAILURE;
}
/** -------------------------------------------------------------
\fn limAdmitControlDeleteSta
\brief Delete all TSPEC for the specified STA
\param tpAniSirGlobal pMac
\param tANI_U16 assocId
\return eSirRetStatus - status
-------------------------------------------------------------*/
tSirRetStatus
limAdmitControlDeleteSta(
tpAniSirGlobal pMac,
tANI_U16 assocId)
{
tpLimTspecInfo pTspecInfo = &pMac->lim.tspecInfo[0];
int ctspec;
for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecInfo++)
{
if (assocId == pTspecInfo->assocId)
{
limTspecDelete(pMac, pTspecInfo);
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("Deleting TSPEC %d for assocId %d"),
ctspec, assocId);
}
}
limLog(pMac, ADMIT_CONTROL_LOGLEVEL, FL("assocId %d done"), assocId);
return eSIR_SUCCESS;
}
/** -------------------------------------------------------------
\fn limAdmitControlInit
\brief init tspec table
\param tpAniSirGlobal pMac
\return eSirRetStatus - status
-------------------------------------------------------------*/
tSirRetStatus limAdmitControlInit(tpAniSirGlobal pMac)
{
vos_mem_set(pMac->lim.tspecInfo, LIM_NUM_TSPEC_MAX * sizeof(tLimTspecInfo), 0);
return eSIR_SUCCESS;
}
/** -------------------------------------------------------------
\fn limUpdateAdmitPolicy
\brief Set the admit control policy based on CFG parameters
\param tpAniSirGlobal pMac
\return eSirRetStatus - status
-------------------------------------------------------------*/
tSirRetStatus limUpdateAdmitPolicy(tpAniSirGlobal pMac)
{
tANI_U32 val;
if (wlan_cfgGetInt(pMac, WNI_CFG_ADMIT_POLICY, &val) != eSIR_SUCCESS)
{
limLog(pMac, LOGP, FL("Unable to get CFG_ADMIT_POLICY"));
return eSIR_FAILURE;
}
pMac->lim.admitPolicyInfo.type = (tANI_U8) val;
if (wlan_cfgGetInt(pMac, WNI_CFG_ADMIT_BWFACTOR, &val) != eSIR_SUCCESS)
{
limLog(pMac, LOGP, FL("Unable to get CFG_ADMIT_BWFACTOR"));
return eSIR_FAILURE;
}
pMac->lim.admitPolicyInfo.bw_factor = (tANI_U8) val;
PELOG1(limLog(pMac, LOG1, FL("LIM: AdmitPolicy %d, bw_factor %d"),
pMac->lim.admitPolicyInfo.type, pMac->lim.admitPolicyInfo.bw_factor);)
return eSIR_SUCCESS;
}
/** -------------------------------------------------------------
\fn limSendHalMsgAddTs
\brief Send halMsg_AddTs to HAL
\param tpAniSirGlobal pMac
\param tANI_U16 staIdx
\param tANI_U8 tspecIdx
\param tSirMacTspecIE tspecIE
\param tSirTclasInfo *tclasInfo
\param tANI_U8 tclasProc
\return eSirRetStatus - status
-------------------------------------------------------------*/
tSirRetStatus
limSendHalMsgAddTs(
tpAniSirGlobal pMac,
tANI_U16 staIdx,
tANI_U8 tspecIdx,
tSirMacTspecIE tspecIE,
tANI_U8 sessionId)
{
tSirMsgQ msg;
tpAddTsParams pAddTsParam;
pAddTsParam = vos_mem_malloc(sizeof(tAddTsParams));
if (NULL == pAddTsParam)
{
PELOGW(limLog(pMac, LOGW, FL("AllocateMemory() failed"));)
return eSIR_MEM_ALLOC_FAILED;
}
vos_mem_set((tANI_U8 *)pAddTsParam, sizeof(tAddTsParams), 0);
pAddTsParam->staIdx = staIdx;
pAddTsParam->tspecIdx = tspecIdx;
vos_mem_copy(&pAddTsParam->tspec, &tspecIE, sizeof(tSirMacTspecIE));
pAddTsParam->sessionId = sessionId;
msg.type = WDA_ADD_TS_REQ;
msg.bodyptr = pAddTsParam;
msg.bodyval = 0;
/* We need to defer any incoming messages until we get a
* WDA_ADD_TS_RSP from HAL.
*/
SET_LIM_PROCESS_DEFD_MESGS(pMac, false);
MTRACE(macTraceMsgTx(pMac, sessionId, msg.type));
if(eSIR_SUCCESS != wdaPostCtrlMsg(pMac, &msg))
{
PELOGW(limLog(pMac, LOGW, FL("wdaPostCtrlMsg() failed"));)
SET_LIM_PROCESS_DEFD_MESGS(pMac, true);
vos_mem_free(pAddTsParam);
return eSIR_FAILURE;
}
return eSIR_SUCCESS;
}
/** -------------------------------------------------------------
\fn limSendHalMsgDelTs
\brief Send halMsg_AddTs to HAL
\param tpAniSirGlobal pMac
\param tANI_U16 staIdx
\param tANI_U8 tspecIdx
\param tSirAddtsReqInfo addts
\return eSirRetStatus - status
-------------------------------------------------------------*/
tSirRetStatus
limSendHalMsgDelTs(
tpAniSirGlobal pMac,
tANI_U16 staIdx,
tANI_U8 tspecIdx,
tSirDeltsReqInfo delts,
tANI_U8 sessionId,
tANI_U8 *bssId)
{
tSirMsgQ msg;
tpDelTsParams pDelTsParam;
pDelTsParam = vos_mem_malloc(sizeof(tDelTsParams));
if (NULL == pDelTsParam)
{
limLog(pMac, LOGP, FL("AllocateMemory() failed"));
return eSIR_MEM_ALLOC_FAILED;
}
msg.type = WDA_DEL_TS_REQ;
msg.bodyptr = pDelTsParam;
msg.bodyval = 0;
vos_mem_set((tANI_U8 *)pDelTsParam, sizeof(tDelTsParams), 0);
//filling message parameters.
pDelTsParam->staIdx = staIdx;
pDelTsParam->tspecIdx = tspecIdx;
vos_mem_copy(&pDelTsParam->bssId, bssId, sizeof(tSirMacAddr));
PELOGW(limLog(pMac, LOGW, FL("calling wdaPostCtrlMsg()"));)
MTRACE(macTraceMsgTx(pMac, sessionId, msg.type));
if(eSIR_SUCCESS != wdaPostCtrlMsg(pMac, &msg))
{
PELOGW(limLog(pMac, LOGW, FL("wdaPostCtrlMsg() failed"));)
vos_mem_free(pDelTsParam);
return eSIR_FAILURE;
}
return eSIR_SUCCESS;
}
/** -------------------------------------------------------------
\fn limProcessHalAddTsRsp
\brief This function process the WDA_ADD_TS_RSP from HAL.
\ If response is successful, then send back SME_ADDTS_RSP.
\ Otherwise, send DELTS action frame to peer and then
\ then send back SME_ADDTS_RSP.
\
\param tpAniSirGlobal pMac
\param tpSirMsgQ limMsg
-------------------------------------------------------------*/
void limProcessHalAddTsRsp(tpAniSirGlobal pMac, tpSirMsgQ limMsg)
{
tpAddTsParams pAddTsRspMsg = NULL;
tpDphHashNode pSta = NULL;
tANI_U16 assocId =0;
tSirMacAddr peerMacAddr;
tANI_U8 rspReqd = 1;
tpPESession psessionEntry = NULL;
/* Need to process all the deferred messages enqueued
* since sending the WDA_ADD_TS_REQ.
*/
SET_LIM_PROCESS_DEFD_MESGS(pMac, true);
if (NULL == limMsg->bodyptr)
{
limLog(pMac, LOGP, FL("Received WDA_ADD_TS_RSP with NULL "));
goto end;
}
pAddTsRspMsg = (tpAddTsParams) (limMsg->bodyptr);
// 090803: Use peFindSessionBySessionId() to obtain the PE session context
// from the sessionId in the Rsp Msg from HAL
psessionEntry = peFindSessionBySessionId(pMac, pAddTsRspMsg->sessionId);
if(psessionEntry == NULL)
{
PELOGE(limLog(pMac, LOGE,FL("Session does Not exist with given sessionId :%d "), pAddTsRspMsg->sessionId);)
limSendSmeAddtsRsp(pMac, rspReqd, eSIR_SME_ADDTS_RSP_FAILED, psessionEntry, pAddTsRspMsg->tspec,
pMac->lim.gLimAddtsReq.sessionId, pMac->lim.gLimAddtsReq.transactionId);
goto end;
}
if(pAddTsRspMsg->status == eHAL_STATUS_SUCCESS)
{
limLog(pMac, LOG1, FL("Received successful ADDTS response from HAL "));
// Use the smesessionId and smetransactionId from the PE session context
limSendSmeAddtsRsp(pMac, rspReqd, eSIR_SME_SUCCESS, psessionEntry, pAddTsRspMsg->tspec,
psessionEntry->smeSessionId, psessionEntry->transactionId);
goto end;
}
else
{
limLog(pMac, LOG1, FL("Received failure ADDTS response from HAL "));
// Send DELTS action frame to AP
// 090803: Get peer MAC addr from session
#if 0
cfgLen = sizeof(tSirMacAddr);
if (wlan_cfgGetStr(pMac, WNI_CFG_BSSID, peerMacAddr, &cfgLen) != eSIR_SUCCESS)
{
limLog(pMac, LOGP, FL("Fail to retrieve BSSID "));
goto end;
}
#endif //TO SUPPORT BT-AMP
sirCopyMacAddr(peerMacAddr,psessionEntry->bssId);
// 090803: Add the SME Session ID
limSendDeltsReqActionFrame(pMac, peerMacAddr, rspReqd, &pAddTsRspMsg->tspec.tsinfo, &pAddTsRspMsg->tspec,
//psessionEntry->smeSessionId);
psessionEntry);
// Delete TSPEC
// 090803: Pull the hash table from the session
pSta = dphLookupAssocId(pMac, pAddTsRspMsg->staIdx, &assocId,
&psessionEntry->dph.dphHashTable);
if (pSta != NULL)
limAdmitControlDeleteTS(pMac, assocId, &pAddTsRspMsg->tspec.tsinfo, NULL, (tANI_U8 *)&pAddTsRspMsg->tspecIdx);
// Send SME_ADDTS_RSP
// 090803: Use the smesessionId and smetransactionId from the PE session context
limSendSmeAddtsRsp(pMac, rspReqd, eSIR_SME_ADDTS_RSP_FAILED, psessionEntry, pAddTsRspMsg->tspec,
psessionEntry->smeSessionId, psessionEntry->transactionId);
goto end;
}
end:
if( pAddTsRspMsg != NULL )
vos_mem_free(pAddTsRspMsg);
return;
}