/* * 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 { tSirMsgQ msg; 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) { tSirMsgQ msg; 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 tpSirMsgQ limMsg -------------------------------------------------------------*/ void lim_process_hal_add_ts_rsp(tpAniSirGlobal pMac, tpSirMsgQ 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; }