| /* |
| * Copyright (c) 2011-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 "sys_def.h" |
| #include "lim_api.h" |
| #include "cfg_api.h" /* wlan_cfg_get_int() */ |
| #include "lim_trace.h" |
| #include "lim_send_sme_rsp_messages.h" |
| #include "lim_types.h" |
| #include "lim_admit_control.h" |
| |
| #define ADMIT_CONTROL_LOGLEVEL LOGD |
| #define ADMIT_CONTROL_POLICY_LOGLEVEL LOGD |
| |
| /* 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) |
| |
| /* ------------------------------------------------------------------------------ */ |
| /* local protos */ |
| |
| static tSirRetStatus |
| lim_calculate_svc_int(tpAniSirGlobal, tSirMacTspecIE *, uint32_t *); |
| static tSirRetStatus |
| lim_validate_tspec_edca(tpAniSirGlobal, tSirMacTspecIE *, tpPESession); |
| static tSirRetStatus |
| lim_validate_tspec(tpAniSirGlobal, tSirMacTspecIE *, tpPESession); |
| static void |
| lim_compute_mean_bw_used(tpAniSirGlobal, uint32_t *, uint32_t, tpLimTspecInfo, |
| tpPESession); |
| static void lim_get_available_bw(tpAniSirGlobal, uint32_t *, uint32_t *, uint32_t, |
| uint32_t); |
| static tSirRetStatus lim_admit_policy_oversubscription(tpAniSirGlobal, |
| tSirMacTspecIE *, |
| tpLimAdmitPolicyInfo, |
| tpLimTspecInfo, |
| tpPESession); |
| static tSirRetStatus lim_tspec_find_by_sta_addr(tpAniSirGlobal, uint8_t *, |
| tSirMacTspecIE *, tpLimTspecInfo, |
| tpLimTspecInfo *); |
| static tSirRetStatus lim_validate_access_policy(tpAniSirGlobal, uint8_t, uint16_t, |
| tpPESession); |
| |
| /** ------------------------------------------------------------- |
| \fn lim_calculate_svc_int |
| \brief TSPEC validation and servcie interval determination |
| \param tpAniSirGlobal pMac |
| \param tSirMacTspecIE *pTspec |
| \param uint32_t *pSvcInt |
| \return eSirRetStatus - status of the comparison |
| -------------------------------------------------------------*/ |
| |
| static tSirRetStatus |
| lim_calculate_svc_int(tpAniSirGlobal pMac, |
| tSirMacTspecIE *pTspec, uint32_t *pSvcInt) |
| { |
| uint32_t 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 { |
| pe_err("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 { |
| pe_err("DataRate not specified"); |
| return eSIR_FAILURE; |
| } |
| |
| *pSvcInt = |
| LIM_CONVERT_SIZE_BITS(msduSz) / LIM_CONVERT_RATE_MBPS(dataRate); |
| return eSIR_FAILURE; |
| } |
| |
| /** |
| * lim_validate_tspec_edca() - Validate the parameters |
| * @mac_ctx: Global MAC context |
| * @tspec: Pointer to the TSPEC |
| * @session_entry: Session Entry |
| * |
| * validate the parameters in the edca tspec |
| * mandatory fields are derived from 11e Annex I (Table I.1) |
| * |
| * Return: Status |
| **/ |
| static tSirRetStatus |
| lim_validate_tspec_edca(tpAniSirGlobal mac_ctx, |
| tSirMacTspecIE *tspec, tpPESession session_entry) |
| { |
| uint32_t max_phy_rate, min_phy_rate; |
| uint32_t phy_mode; |
| tSirRetStatus retval = eSIR_SUCCESS; |
| |
| lim_get_phy_mode(mac_ctx, &phy_mode, session_entry); |
| |
| lim_get_available_bw(mac_ctx, &max_phy_rate, &min_phy_rate, phy_mode, |
| 1 /* bandwidth mult factor */); |
| /* mandatory fields are derived from 11e Annex I (Table I.1) */ |
| if ((tspec->nomMsduSz == 0) || |
| (tspec->meanDataRate == 0) || |
| (tspec->surplusBw == 0) || |
| (tspec->minPhyRate == 0) || |
| (tspec->minPhyRate > max_phy_rate)) { |
| pe_warn("Invalid EDCA Tspec: NomMsdu: %d meanDataRate: %d surplusBw: %d min_phy_rate: %d", |
| tspec->nomMsduSz, tspec->meanDataRate, |
| tspec->surplusBw, tspec->minPhyRate); |
| retval = eSIR_FAILURE; |
| } |
| |
| pe_debug("return status: %d", retval); |
| return retval; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_validate_tspec |
| \brief validate the offered tspec |
| \param tpAniSirGlobal pMac |
| \param tSirMacTspecIE *pTspec |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| |
| static tSirRetStatus |
| lim_validate_tspec(tpAniSirGlobal pMac, |
| tSirMacTspecIE *pTspec, tpPESession psessionEntry) |
| { |
| tSirRetStatus retval = eSIR_SUCCESS; |
| |
| switch (pTspec->tsinfo.traffic.accessPolicy) { |
| case SIR_MAC_ACCESSPOLICY_EDCA: |
| retval = lim_validate_tspec_edca(pMac, pTspec, psessionEntry); |
| if (retval != eSIR_SUCCESS) |
| pe_warn("EDCA tspec invalid"); |
| break; |
| |
| case SIR_MAC_ACCESSPOLICY_HCCA: |
| case SIR_MAC_ACCESSPOLICY_BOTH: |
| /* TBD: should we support hybrid tspec as well?? for now, just fall through */ |
| default: |
| pe_warn("AccessType: %d not supported", |
| pTspec->tsinfo.traffic.accessPolicy); |
| retval = eSIR_FAILURE; |
| break; |
| } |
| return retval; |
| } |
| |
| /* ----------------------------------------------------------------------------- */ |
| /* Admit Control Policy */ |
| |
| /** ------------------------------------------------------------- |
| \fn lim_compute_mean_bw_used |
| \brief determime the used/allocated bandwidth |
| \param tpAniSirGlobal pMac |
| \param uint32_t *pBw |
| \param uint32_t phyMode |
| \param tpLimTspecInfo pTspecInfo |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| |
| static void |
| lim_compute_mean_bw_used(tpAniSirGlobal pMac, |
| uint32_t *pBw, |
| uint32_t phyMode, |
| tpLimTspecInfo pTspecInfo, tpPESession psessionEntry) |
| { |
| uint32_t ctspec; |
| *pBw = 0; |
| for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecInfo++) { |
| if (pTspecInfo->inuse) { |
| tpDphHashNode pSta = |
| dph_get_hash_entry(pMac, pTspecInfo->assocId, |
| &psessionEntry->dph.dphHashTable); |
| if (pSta == NULL) { |
| /* maybe we should delete the tspec?? */ |
| pe_err("Tspec: %d assocId: %d dphNode not found", |
| ctspec, pTspecInfo->assocId); |
| continue; |
| } |
| *pBw += pTspecInfo->tspec.meanDataRate; |
| } |
| } |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_get_available_bw |
| \brief based on the phy mode and the bw_factor, determine the total bandwidth that |
| can be supported |
| \param tpAniSirGlobal pMac |
| \param uint32_t *pMaxBw |
| \param uint32_t *pMinBw |
| \param uint32_t phyMode |
| \param uint32_t bw_factor |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| |
| static void |
| lim_get_available_bw(tpAniSirGlobal pMac, |
| uint32_t *pMaxBw, |
| uint32_t *pMinBw, uint32_t phyMode, uint32_t 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; |
| } |
| |
| /** |
| * lim_admit_policy_oversubscription() - Admission control policy |
| * @mac_ctx: Global MAC Context |
| * @tspec: Pointer to the tspec |
| * @admit_policy: Admission policy |
| * @tspec_info: TSPEC information |
| * @session_entry: Session Entry |
| * |
| * 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. |
| * |
| * Return: Status |
| **/ |
| static tSirRetStatus |
| lim_admit_policy_oversubscription(tpAniSirGlobal mac_ctx, |
| tSirMacTspecIE *tspec, |
| tpLimAdmitPolicyInfo admit_policy, |
| tpLimTspecInfo tspec_info, |
| tpPESession session_entry) |
| { |
| uint32_t totalbw, minbw, usedbw; |
| uint32_t phy_mode; |
| |
| /* determine total bandwidth used so far */ |
| lim_get_phy_mode(mac_ctx, &phy_mode, session_entry); |
| |
| lim_compute_mean_bw_used(mac_ctx, &usedbw, phy_mode, |
| tspec_info, session_entry); |
| |
| /* determine how much bw is available based on the current phy mode */ |
| lim_get_available_bw(mac_ctx, &totalbw, &minbw, phy_mode, |
| admit_policy->bw_factor); |
| |
| if (usedbw > totalbw) /* this can't possibly happen */ |
| return eSIR_FAILURE; |
| |
| if ((totalbw - usedbw) < tspec->meanDataRate) { |
| pe_warn("Total BW: %d Used: %d Tspec request: %d not possible", |
| totalbw, usedbw, tspec->meanDataRate); |
| return eSIR_FAILURE; |
| } |
| return eSIR_SUCCESS; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_admit_policy |
| \brief determine the current admit control policy and apply it for the offered tspec |
| \param tpAniSirGlobal pMac |
| \param tSirMacTspecIE *pTspec |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| |
| static tSirRetStatus lim_admit_policy(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 = lim_admit_policy_oversubscription(pMac, pTspec, |
| &pMac->lim. |
| admitPolicyInfo, |
| &pMac->lim.tspecInfo[0], |
| psessionEntry); |
| if (retval != eSIR_SUCCESS) |
| pe_err("rejected by BWFactor policy"); |
| break; |
| |
| case WNI_CFG_ADMIT_POLICY_REJECT_ALL: |
| retval = eSIR_FAILURE; |
| break; |
| |
| default: |
| retval = eSIR_SUCCESS; |
| pe_warn("Admit Policy: %d unknown, admitting all traffic", |
| pAdmitPolicy->type); |
| break; |
| } |
| return retval; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_tspec_delete |
| \brief delete the specified tspec |
| \param tpAniSirGlobal pMac |
| \param tpLimTspecInfo pInfo |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| |
| /* ----------------------------------------------------------------------------- */ |
| /* delete the specified tspec */ |
| static void lim_tspec_delete(tpAniSirGlobal pMac, tpLimTspecInfo pInfo) |
| { |
| if (pInfo == NULL) |
| return; |
| /* pierre */ |
| pe_debug("tspec entry: %d delete tspec: %pK", pInfo->idx, pInfo); |
| pInfo->inuse = 0; |
| |
| return; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_tspec_find_by_sta_addr |
| \brief Send halMsg_AddTs to HAL |
| \param tpAniSirGlobal pMac |
| \param \param uint8_t *pAddr |
| \param tSirMacTspecIE *pTspecIE |
| \param tpLimTspecInfo pTspecList |
| \param tpLimTspecInfo *ppInfo |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| |
| /* find the specified tspec in the list */ |
| static tSirRetStatus |
| lim_tspec_find_by_sta_addr(tpAniSirGlobal pMac, |
| uint8_t *pAddr, |
| tSirMacTspecIE *pTspecIE, |
| tpLimTspecInfo pTspecList, tpLimTspecInfo *ppInfo) |
| { |
| int ctspec; |
| |
| *ppInfo = NULL; |
| |
| for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecList++) { |
| if ((pTspecList->inuse) |
| && |
| (!qdf_mem_cmp |
| (pAddr, pTspecList->staAddr, sizeof(pTspecList->staAddr))) |
| && |
| (!qdf_mem_cmp |
| ((uint8_t *) pTspecIE, (uint8_t *) &pTspecList->tspec, |
| sizeof(tSirMacTspecIE)))) { |
| *ppInfo = pTspecList; |
| return eSIR_SUCCESS; |
| } |
| } |
| return eSIR_FAILURE; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_tspec_find_by_assoc_id |
| \brief find tspec with matchin staid and Tspec |
| \param tpAniSirGlobal pMac |
| \param uint32_t staid |
| \param tSirMacTspecIE *pTspecIE |
| \param tpLimTspecInfo pTspecList |
| \param tpLimTspecInfo *ppInfo |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| |
| tSirRetStatus |
| lim_tspec_find_by_assoc_id(tpAniSirGlobal pMac, |
| uint16_t assocId, |
| tSirMacTspecIE *pTspecIE, |
| tpLimTspecInfo pTspecList, tpLimTspecInfo *ppInfo) |
| { |
| int ctspec; |
| |
| *ppInfo = NULL; |
| |
| pe_debug("Trying to find tspec entry for assocId: %d pTsInfo->traffic.direction: %d pTsInfo->traffic.tsid: %d", |
| assocId, pTspecIE->tsinfo.traffic.direction, |
| pTspecIE->tsinfo.traffic.tsid); |
| |
| for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecList++) { |
| if ((pTspecList->inuse) |
| && (assocId == pTspecList->assocId) |
| && |
| (!qdf_mem_cmp |
| ((uint8_t *) pTspecIE, (uint8_t *) &pTspecList->tspec, |
| sizeof(tSirMacTspecIE)))) { |
| *ppInfo = pTspecList; |
| return eSIR_SUCCESS; |
| } |
| } |
| return eSIR_FAILURE; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_find_tspec |
| \brief finding a TSPEC entry with assocId, tsinfo.direction and tsinfo.tsid |
| \param uint16_t assocId |
| \param tpAniSirGlobal pMac |
| \param tSirMacTSInfo *pTsInfo |
| \param tpLimTspecInfo pTspecList |
| \param tpLimTspecInfo *ppInfo |
| \return eSirRetStatus - status of the comparison |
| -------------------------------------------------------------*/ |
| |
| static tSirRetStatus |
| lim_find_tspec(tpAniSirGlobal pMac, |
| uint16_t assocId, |
| tSirMacTSInfo *pTsInfo, |
| tpLimTspecInfo pTspecList, tpLimTspecInfo *ppInfo) |
| { |
| int ctspec; |
| |
| *ppInfo = NULL; |
| |
| pe_debug("Trying to find tspec entry for assocId: %d pTsInfo->traffic.direction: %d pTsInfo->traffic.tsid: %d", |
| assocId, 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 lim_tspec_add |
| \brief add or update the specified tspec to the tspec list |
| \param tpAniSirGlobal pMac |
| \param uint8_t *pAddr |
| \param uint16_t assocId |
| \param tSirMacTspecIE *pTspec |
| \param uint32_t interval |
| \param tpLimTspecInfo *ppInfo |
| |
| \return eSirRetStatus - status of the comparison |
| -------------------------------------------------------------*/ |
| |
| tSirRetStatus lim_tspec_add(tpAniSirGlobal pMac, |
| uint8_t *pAddr, |
| uint16_t assocId, |
| tSirMacTspecIE *pTspec, |
| uint32_t interval, tpLimTspecInfo *ppInfo) |
| { |
| tpLimTspecInfo pTspecList = &pMac->lim.tspecInfo[0]; |
| *ppInfo = NULL; |
| |
| /* validate the assocId */ |
| if (assocId >= pMac->lim.maxStation) { |
| pe_err("Invalid assocId 0x%x", assocId); |
| return eSIR_FAILURE; |
| } |
| /* decide whether to add/update */ |
| { |
| *ppInfo = NULL; |
| |
| if (eSIR_SUCCESS == |
| lim_find_tspec(pMac, assocId, &pTspec->tsinfo, pTspecList, |
| ppInfo)) { |
| /* update this entry. */ |
| pe_debug("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 |
| */ |
| uint8_t ctspec = 0; |
| |
| for (ctspec = 0, pTspecList = &pMac->lim.tspecInfo[0]; |
| ctspec < LIM_NUM_TSPEC_MAX; |
| ctspec++, pTspecList++) { |
| if (!pTspecList->inuse) { |
| pe_debug("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; |
| qdf_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; |
| pe_debug("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. |
| */ |
| pTspecList->inuse = 1; |
| *ppInfo = pTspecList; |
| pe_debug("added entry for HCCA AccessPolicy"); |
| return eSIR_SUCCESS; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_validate_access_policy |
| \brief Validates Access policy |
| \param tpAniSirGlobal pMac |
| \param uint8_t accessPolicy |
| \param uint16_t assocId |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| |
| static tSirRetStatus |
| lim_validate_access_policy(tpAniSirGlobal pMac, |
| uint8_t accessPolicy, |
| uint16_t assocId, tpPESession psessionEntry) |
| { |
| tSirRetStatus retval = eSIR_FAILURE; |
| tpDphHashNode pSta = |
| dph_get_hash_entry(pMac, assocId, &psessionEntry->dph.dphHashTable); |
| |
| if ((pSta == NULL) || (!pSta->valid)) { |
| pe_err("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: |
| default: |
| pe_err("Invalid accessPolicy: %d", |
| accessPolicy); |
| break; |
| } |
| |
| if (retval != eSIR_SUCCESS) |
| pe_warn("accPol: %d staId: %d lle: %d wme: %d wsm: %d", |
| accessPolicy, pSta->staIndex, pSta->lleEnabled, |
| pSta->wmeEnabled, pSta->wsmEnabled); |
| |
| return retval; |
| } |
| |
| /** |
| * lim_admit_control_add_ts() - Check if STA can be admitted |
| * @pMac: Global MAC context |
| * @pAddr: Address |
| * @pAddts: ADD TS |
| * @pQos: QOS fields |
| * @assocId: Association ID |
| * @alloc: Allocate bandwidth for this tspec |
| * @pSch: Schedule IE |
| * @pTspecIdx: TSPEC index |
| * @psessionEntry: PE Session Entry |
| * |
| * Determine if STA with the specified TSPEC can be admitted. If it can, |
| * a schedule element is provided |
| * |
| * Return: status |
| **/ |
| tSirRetStatus lim_admit_control_add_ts(tpAniSirGlobal pMac, uint8_t *pAddr, |
| tSirAddtsReqInfo *pAddts, tSirMacQosCapabilityStaIE *pQos, |
| uint16_t assocId, uint8_t alloc, tSirMacScheduleIE *pSch, |
| uint8_t *pTspecIdx, tpPESession psessionEntry) |
| { |
| tpLimTspecInfo pTspecInfo; |
| tSirRetStatus retval; |
| uint32_t 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. */ |
| |
| pe_debug("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) |
| ? lim_tspec_find_by_assoc_id(pMac, assocId, &pAddts->tspec, |
| &pMac->lim.tspecInfo[0], &pTspecInfo) |
| : lim_tspec_find_by_sta_addr(pMac, pAddr, &pAddts->tspec, |
| &pMac->lim.tspecInfo[0], &pTspecInfo); |
| |
| if (retval == eSIR_SUCCESS) { |
| pe_err("duplicate tspec index: %d", pTspecInfo->idx); |
| return eSIR_FAILURE; |
| } |
| /* check that the tspec's are well formed and acceptable */ |
| if (lim_validate_tspec(pMac, &pAddts->tspec, psessionEntry) != |
| eSIR_SUCCESS) { |
| pe_warn("tspec validation failed"); |
| return eSIR_FAILURE; |
| } |
| /* determine a service interval for the tspec */ |
| if (lim_calculate_svc_int(pMac, &pAddts->tspec, &svcInterval) != |
| eSIR_SUCCESS) { |
| pe_warn("SvcInt calculate failed"); |
| return eSIR_FAILURE; |
| } |
| /* determine if the tspec can be admitted or not based on current policy */ |
| if (lim_admit_policy(pMac, &pAddts->tspec, psessionEntry) != eSIR_SUCCESS) { |
| pe_warn("tspec rejected by admit control policy"); |
| return eSIR_FAILURE; |
| } |
| /* fill in a schedule if requested */ |
| if (pSch != NULL) { |
| qdf_mem_set((uint8_t *) pSch, sizeof(*pSch), 0); |
| pSch->svcStartTime = pAddts->tspec.svcStartTime; |
| pSch->svcInterval = svcInterval; |
| pSch->maxSvcDuration = (uint16_t) 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 (lim_validate_access_policy |
| (pMac, (uint8_t) pAddts->tspec.tsinfo.traffic.accessPolicy, assocId, |
| psessionEntry) != eSIR_SUCCESS) { |
| pe_warn("AccessPolicy: %d is not valid in current mode", |
| pAddts->tspec.tsinfo.traffic.accessPolicy); |
| return eSIR_FAILURE; |
| } |
| /* add tspec to list */ |
| if (lim_tspec_add |
| (pMac, pAddr, assocId, &pAddts->tspec, svcInterval, &pTspecInfo) |
| != eSIR_SUCCESS) { |
| pe_err("no space in tspec list"); |
| return eSIR_FAILURE; |
| } |
| /* passing lim tspec table index to the caller */ |
| *pTspecIdx = pTspecInfo->idx; |
| |
| return eSIR_SUCCESS; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_admit_control_delete_ts |
| \brief Delete the specified Tspec for the specified STA |
| \param tpAniSirGlobal pMac |
| \param uint16_t assocId |
| \param tSirMacTSInfo *pTsInfo |
| \param uint8_t *pTsStatus |
| \param uint8_t *ptspecIdx |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| |
| tSirRetStatus |
| lim_admit_control_delete_ts(tpAniSirGlobal pMac, |
| uint16_t assocId, |
| tSirMacTSInfo *pTsInfo, |
| uint8_t *pTsStatus, uint8_t *ptspecIdx) |
| { |
| tpLimTspecInfo pTspecInfo = NULL; |
| |
| if (pTsStatus != NULL) |
| *pTsStatus = 0; |
| |
| if (lim_find_tspec |
| (pMac, assocId, pTsInfo, &pMac->lim.tspecInfo[0], |
| &pTspecInfo) == eSIR_SUCCESS) { |
| if (pTspecInfo != NULL) { |
| pe_debug("Tspec entry: %d found", pTspecInfo->idx); |
| |
| *ptspecIdx = pTspecInfo->idx; |
| lim_tspec_delete(pMac, pTspecInfo); |
| return eSIR_SUCCESS; |
| } |
| } |
| return eSIR_FAILURE; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_admit_control_delete_sta |
| \brief Delete all TSPEC for the specified STA |
| \param tpAniSirGlobal pMac |
| \param uint16_t assocId |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| |
| tSirRetStatus lim_admit_control_delete_sta(tpAniSirGlobal pMac, uint16_t assocId) |
| { |
| tpLimTspecInfo pTspecInfo = &pMac->lim.tspecInfo[0]; |
| int ctspec; |
| |
| for (ctspec = 0; ctspec < LIM_NUM_TSPEC_MAX; ctspec++, pTspecInfo++) { |
| if (assocId == pTspecInfo->assocId) { |
| lim_tspec_delete(pMac, pTspecInfo); |
| pe_debug("Deleting TSPEC: %d for assocId: %d", ctspec, |
| assocId); |
| } |
| } |
| pe_debug("assocId: %d done", assocId); |
| |
| return eSIR_SUCCESS; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_admit_control_init |
| \brief init tspec table |
| \param tpAniSirGlobal pMac |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| tSirRetStatus lim_admit_control_init(tpAniSirGlobal pMac) |
| { |
| qdf_mem_set(pMac->lim.tspecInfo, |
| LIM_NUM_TSPEC_MAX * sizeof(tLimTspecInfo), 0); |
| return eSIR_SUCCESS; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_update_admit_policy |
| \brief Set the admit control policy based on CFG parameters |
| \param tpAniSirGlobal pMac |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| |
| tSirRetStatus lim_update_admit_policy(tpAniSirGlobal pMac) |
| { |
| uint32_t val; |
| |
| if (wlan_cfg_get_int(pMac, WNI_CFG_ADMIT_POLICY, &val) != eSIR_SUCCESS) { |
| pe_err("Unable to get CFG_ADMIT_POLICY"); |
| return eSIR_FAILURE; |
| } |
| pMac->lim.admitPolicyInfo.type = (uint8_t) val; |
| if (wlan_cfg_get_int(pMac, WNI_CFG_ADMIT_BWFACTOR, &val) != eSIR_SUCCESS) { |
| pe_err("Unable to get CFG_ADMIT_BWFACTOR"); |
| return eSIR_FAILURE; |
| } |
| pMac->lim.admitPolicyInfo.bw_factor = (uint8_t) val; |
| |
| pe_debug("LIM: AdmitPolicy: %d bw_factor: %d", |
| pMac->lim.admitPolicyInfo.type, |
| pMac->lim.admitPolicyInfo.bw_factor); |
| |
| return eSIR_SUCCESS; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_send_hal_msg_add_ts |
| \brief Send halMsg_AddTs to HAL |
| \param tpAniSirGlobal pMac |
| \param uint16_t staIdx |
| \param uint8_t tspecIdx |
| \param tSirMacTspecIE tspecIE |
| \param tSirTclasInfo *tclasInfo |
| \param uint8_t tclasProc |
| \param uint16_t tsm_interval |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| #ifdef FEATURE_WLAN_ESE |
| tSirRetStatus |
| lim_send_hal_msg_add_ts(tpAniSirGlobal pMac, |
| uint16_t staIdx, |
| uint8_t tspecIdx, |
| tSirMacTspecIE tspecIE, |
| uint8_t sessionId, uint16_t tsm_interval) |
| #else |
| tSirRetStatus |
| lim_send_hal_msg_add_ts(tpAniSirGlobal pMac, |
| uint16_t staIdx, |
| uint8_t tspecIdx, tSirMacTspecIE tspecIE, uint8_t sessionId) |
| #endif |
| { |
| struct scheduler_msg msg = {0}; |
| tpAddTsParams pAddTsParam; |
| |
| tpPESession psessionEntry = pe_find_session_by_session_id(pMac, sessionId); |
| |
| if (psessionEntry == NULL) { |
| pe_err("Unable to get Session for session Id: %d", |
| sessionId); |
| return eSIR_FAILURE; |
| } |
| |
| pAddTsParam = qdf_mem_malloc(sizeof(tAddTsParams)); |
| if (NULL == pAddTsParam) { |
| pe_err("AllocateMemory() failed"); |
| return eSIR_MEM_ALLOC_FAILED; |
| } |
| |
| pAddTsParam->staIdx = staIdx; |
| pAddTsParam->tspecIdx = tspecIdx; |
| qdf_mem_copy(&pAddTsParam->tspec, &tspecIE, sizeof(tSirMacTspecIE)); |
| pAddTsParam->sessionId = sessionId; |
| pAddTsParam->sme_session_id = psessionEntry->smeSessionId; |
| |
| #ifdef FEATURE_WLAN_ESE |
| pAddTsParam->tsm_interval = tsm_interval; |
| #endif |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| if (pMac->roam.configParam.isRoamOffloadEnabled && |
| psessionEntry->is11Rconnection) |
| pAddTsParam->setRICparams = 1; |
| #endif |
| |
| msg.type = WMA_ADD_TS_REQ; |
| msg.bodyptr = pAddTsParam; |
| msg.bodyval = 0; |
| |
| /* We need to defer any incoming messages until we get a |
| * WMA_ADD_TS_RSP from HAL. |
| */ |
| SET_LIM_PROCESS_DEFD_MESGS(pMac, false); |
| MTRACE(mac_trace_msg_tx(pMac, sessionId, msg.type)); |
| |
| if (eSIR_SUCCESS != wma_post_ctrl_msg(pMac, &msg)) { |
| pe_warn("wma_post_ctrl_msg() failed"); |
| SET_LIM_PROCESS_DEFD_MESGS(pMac, true); |
| qdf_mem_free(pAddTsParam); |
| return eSIR_FAILURE; |
| } |
| return eSIR_SUCCESS; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_send_hal_msg_del_ts |
| \brief Send halMsg_AddTs to HAL |
| \param tpAniSirGlobal pMac |
| \param uint16_t staIdx |
| \param uint8_t tspecIdx |
| \param tSirAddtsReqInfo addts |
| \return eSirRetStatus - status |
| -------------------------------------------------------------*/ |
| |
| tSirRetStatus |
| lim_send_hal_msg_del_ts(tpAniSirGlobal pMac, |
| uint16_t staIdx, |
| uint8_t tspecIdx, |
| tSirDeltsReqInfo delts, uint8_t sessionId, uint8_t *bssId) |
| { |
| struct scheduler_msg msg = {0}; |
| tpDelTsParams pDelTsParam; |
| tpPESession psessionEntry = NULL; |
| |
| pDelTsParam = qdf_mem_malloc(sizeof(tDelTsParams)); |
| if (NULL == pDelTsParam) { |
| pe_err("AllocateMemory() failed"); |
| return eSIR_MEM_ALLOC_FAILED; |
| } |
| |
| msg.type = WMA_DEL_TS_REQ; |
| msg.bodyptr = pDelTsParam; |
| msg.bodyval = 0; |
| |
| /* filling message parameters. */ |
| pDelTsParam->staIdx = staIdx; |
| pDelTsParam->tspecIdx = tspecIdx; |
| qdf_mem_copy(&pDelTsParam->bssId, bssId, sizeof(tSirMacAddr)); |
| |
| psessionEntry = pe_find_session_by_session_id(pMac, sessionId); |
| if (psessionEntry == NULL) { |
| pe_err("Session does Not exist with given sessionId: %d", |
| sessionId); |
| goto err; |
| } |
| pDelTsParam->sessionId = psessionEntry->smeSessionId; |
| pDelTsParam->userPrio = delts.wmeTspecPresent ? |
| delts.tspec.tsinfo.traffic.userPrio : |
| delts.tsinfo.traffic.userPrio; |
| |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| if (pMac->roam.configParam.isRoamOffloadEnabled && |
| psessionEntry->is11Rconnection) { |
| qdf_mem_copy(&pDelTsParam->delTsInfo, &delts, |
| sizeof(tSirDeltsReqInfo)); |
| pDelTsParam->setRICparams = 1; |
| } |
| #endif |
| MTRACE(mac_trace_msg_tx(pMac, sessionId, msg.type)); |
| |
| if (eSIR_SUCCESS != wma_post_ctrl_msg(pMac, &msg)) { |
| pe_warn("wma_post_ctrl_msg() failed"); |
| goto err; |
| } |
| return eSIR_SUCCESS; |
| |
| err: |
| qdf_mem_free(pDelTsParam); |
| return eSIR_FAILURE; |
| } |
| |
| /** ------------------------------------------------------------- |
| \fn lim_process_hal_add_ts_rsp |
| \brief This function process the WMA_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 struct scheduler_msg *limMsg |
| -------------------------------------------------------------*/ |
| void lim_process_hal_add_ts_rsp(tpAniSirGlobal pMac, |
| struct scheduler_msg *limMsg) |
| { |
| tpAddTsParams pAddTsRspMsg = NULL; |
| tpDphHashNode pSta = NULL; |
| uint16_t assocId = 0; |
| tSirMacAddr peerMacAddr; |
| uint8_t rspReqd = 1; |
| tpPESession psessionEntry = NULL; |
| |
| /* Need to process all the deferred messages enqueued |
| * since sending the WMA_ADD_TS_REQ. |
| */ |
| SET_LIM_PROCESS_DEFD_MESGS(pMac, true); |
| |
| if (NULL == limMsg->bodyptr) { |
| pe_err("Received WMA_ADD_TS_RSP with NULL"); |
| goto end; |
| } |
| |
| pAddTsRspMsg = (tpAddTsParams) (limMsg->bodyptr); |
| |
| /* 090803: Use pe_find_session_by_session_id() to obtain the PE session context */ |
| /* from the sessionId in the Rsp Msg from HAL */ |
| psessionEntry = pe_find_session_by_session_id(pMac, pAddTsRspMsg->sessionId); |
| |
| if (psessionEntry == NULL) { |
| pe_err("Session does Not exist with given sessionId: %d", |
| pAddTsRspMsg->sessionId); |
| lim_send_sme_addts_rsp(pMac, rspReqd, eSIR_SME_ADDTS_RSP_FAILED, |
| psessionEntry, pAddTsRspMsg->tspec, |
| pMac->lim.gLimAddtsReq.sessionId, |
| pMac->lim.gLimAddtsReq.transactionId); |
| goto end; |
| } |
| |
| if (pAddTsRspMsg->status == QDF_STATUS_SUCCESS) { |
| pe_debug("Received successful ADDTS response from HAL"); |
| /* Use the smesessionId and smetransactionId from the PE session context */ |
| lim_send_sme_addts_rsp(pMac, rspReqd, eSIR_SME_SUCCESS, |
| psessionEntry, pAddTsRspMsg->tspec, |
| psessionEntry->smeSessionId, |
| psessionEntry->transactionId); |
| goto end; |
| } else { |
| pe_debug("Received failure ADDTS response from HAL"); |
| /* Send DELTS action frame to AP */ |
| /* 090803: Get peer MAC addr from session */ |
| sir_copy_mac_addr(peerMacAddr, psessionEntry->bssId); |
| |
| /* 090803: Add the SME Session ID */ |
| lim_send_delts_req_action_frame(pMac, peerMacAddr, rspReqd, |
| &pAddTsRspMsg->tspec.tsinfo, |
| &pAddTsRspMsg->tspec, psessionEntry); |
| |
| /* Delete TSPEC */ |
| /* 090803: Pull the hash table from the session */ |
| pSta = dph_lookup_assoc_id(pMac, pAddTsRspMsg->staIdx, &assocId, |
| &psessionEntry->dph.dphHashTable); |
| if (pSta != NULL) |
| lim_admit_control_delete_ts(pMac, assocId, |
| &pAddTsRspMsg->tspec.tsinfo, |
| NULL, |
| (uint8_t *) &pAddTsRspMsg-> |
| tspecIdx); |
| |
| /* Send SME_ADDTS_RSP */ |
| /* 090803: Use the smesessionId and smetransactionId from the PE session context */ |
| lim_send_sme_addts_rsp(pMac, rspReqd, eSIR_SME_ADDTS_RSP_FAILED, |
| psessionEntry, pAddTsRspMsg->tspec, |
| psessionEntry->smeSessionId, |
| psessionEntry->transactionId); |
| goto end; |
| } |
| |
| end: |
| if (pAddTsRspMsg != NULL) |
| qdf_mem_free(pAddTsRspMsg); |
| return; |
| } |