| /* |
| * Copyright (c) 2012-2014, 2016-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. |
| */ |
| |
| /**========================================================================= |
| |
| \file sme_Qos.c |
| |
| \brief implementation for SME QoS APIs |
| |
| ========================================================================*/ |
| /* $Header$ */ |
| /*-------------------------------------------------------------------------- |
| Include Files |
| ------------------------------------------------------------------------*/ |
| |
| |
| #include "aniGlobal.h" |
| |
| #include "smeInside.h" |
| #include "vos_diag_core_event.h" |
| #include "vos_diag_core_log.h" |
| |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| #include "smsDebug.h" |
| #include "utilsParser.h" |
| #endif |
| #if defined(FEATURE_WLAN_ESE) && !defined(FEATURE_WLAN_ESE_UPLOAD) |
| #include <csrEse.h> |
| #endif |
| |
| #ifndef WLAN_MDM_CODE_REDUCTION_OPT |
| /* TODO : 6Mbps as Cisco APs seem to like only this value; analysis req. */ |
| #define SME_QOS_MIN_PHY_RATE 0x5B8D80 |
| #define SME_QOS_SURPLUS_BW_ALLOWANCE 0x2000 /* Ratio of 1.0 */ |
| /*--------------------------------------------------------------------------- |
| Max values to bound tspec params against and avoid rollover |
| ---------------------------------------------------------------------------*/ |
| #define SME_QOS_32BIT_MAX 0xFFFFFFFF |
| #define SME_QOS_16BIT_MAX 0xFFFF |
| #define SME_QOS_16BIT_MSB 0x8000 |
| /*--------------------------------------------------------------------------- |
| Adds y to x, but saturates at 32-bit max to avoid rollover |
| ---------------------------------------------------------------------------*/ |
| #define SME_QOS_BOUNDED_U32_ADD_Y_TO_X( _x, _y ) \ |
| do \ |
| { \ |
| (_x) = ( (SME_QOS_32BIT_MAX-(_x))<(_y) ) ? \ |
| (SME_QOS_32BIT_MAX) : (_x)+(_y); \ |
| } while(0) |
| /*--------------------------------------------------------------------------- |
| As per WMM spec there could be max 2 TSPEC running on the same AC with |
| different direction. We will refer each TSPEC with an index |
| ---------------------------------------------------------------------------*/ |
| #define SME_QOS_TSPEC_INDEX_0 0 |
| #define SME_QOS_TSPEC_INDEX_1 1 |
| #define SME_QOS_TSPEC_INDEX_MAX 2 |
| #define SME_QOS_TSPEC_MASK_BIT_1_SET 1 |
| #define SME_QOS_TSPEC_MASK_BIT_2_SET 2 |
| #define SME_QOS_TSPEC_MASK_BIT_1_2_SET 3 |
| #define SME_QOS_TSPEC_MASK_CLEAR 0 |
| |
| /* Which key to search on, in the flow list (1 = flowID, 2 = AC, 4 = reason) */ |
| #define SME_QOS_SEARCH_KEY_INDEX_1 1 |
| #define SME_QOS_SEARCH_KEY_INDEX_2 2 |
| #define SME_QOS_SEARCH_KEY_INDEX_3 4 |
| #define SME_QOS_SEARCH_KEY_INDEX_4 8 // ac + direction |
| #define SME_QOS_SEARCH_KEY_INDEX_5 0x10 // ac + tspec_mask |
| //special value for searching any Session Id |
| #define SME_QOS_SEARCH_SESSION_ID_ANY CSR_ROAM_SESSION_MAX |
| #define SME_QOS_ACCESS_POLICY_EDCA 1 |
| #define SME_QOS_MAX_TID 255 |
| #define SME_QOS_TSPEC_IE_LENGTH 61 |
| #define SME_QOS_TSPEC_IE_TYPE 2 |
| #define SME_QOS_MIN_FLOW_ID 1 |
| #define SME_QOS_MAX_FLOW_ID 0xFFFFFFFE |
| #define SME_QOS_INVALID_FLOW_ID 0xFFFFFFFF |
| // per the WMM Specification v1.2 Section 2.2.10 |
| // The Dialog Token field shall be set [...] to a non-zero value |
| #define SME_QOS_MIN_DIALOG_TOKEN 1 |
| #define SME_QOS_MAX_DIALOG_TOKEN 0xFF |
| /*-------------------------------------------------------------------------- |
| Type declarations |
| ------------------------------------------------------------------------*/ |
| /*--------------------------------------------------------------------------- |
| Enumeration of the various states in the QoS state m/c |
| ---------------------------------------------------------------------------*/ |
| typedef enum |
| { |
| SME_QOS_CLOSED = 0, |
| SME_QOS_INIT, |
| SME_QOS_LINK_UP, |
| SME_QOS_REQUESTED, |
| SME_QOS_QOS_ON, |
| SME_QOS_HANDOFF, |
| |
| }sme_QosStates; |
| /*--------------------------------------------------------------------------- |
| Enumeration of the various Release QoS trigger |
| ---------------------------------------------------------------------------*/ |
| typedef enum |
| { |
| SME_QOS_RELEASE_DEFAULT = 0, |
| SME_QOS_RELEASE_BY_AP, |
| }sme_QosRelTriggers; |
| /*--------------------------------------------------------------------------- |
| Enumeration of the various QoS cmds |
| ---------------------------------------------------------------------------*/ |
| typedef enum |
| { |
| SME_QOS_SETUP_REQ = 0, |
| SME_QOS_RELEASE_REQ, |
| SME_QOS_MODIFY_REQ, |
| SME_QOS_RESEND_REQ, |
| SME_QOS_CMD_MAX |
| }sme_QosCmdType; |
| /*--------------------------------------------------------------------------- |
| Enumeration of the various QoS reason codes to be used in the Flow list |
| ---------------------------------------------------------------------------*/ |
| typedef enum |
| { |
| SME_QOS_REASON_SETUP = 0, |
| SME_QOS_REASON_RELEASE, |
| SME_QOS_REASON_MODIFY, |
| SME_QOS_REASON_MODIFY_PENDING, |
| SME_QOS_REASON_REQ_SUCCESS, |
| SME_QOS_REASON_MAX |
| }sme_QosReasonType; |
| |
| /*--------------------------------------------------------------------------- |
| Table to map user priority passed in as an argument to appropriate Access |
| Category as specified in 802.11e/WMM |
| ---------------------------------------------------------------------------*/ |
| sme_QosEdcaAcType sme_QosUPtoACMap[SME_QOS_WMM_UP_MAX] = |
| { |
| SME_QOS_EDCA_AC_BE, /* User Priority 0 */ |
| SME_QOS_EDCA_AC_BK, /* User Priority 1 */ |
| SME_QOS_EDCA_AC_BK, /* User Priority 2 */ |
| SME_QOS_EDCA_AC_BE, /* User Priority 3 */ |
| SME_QOS_EDCA_AC_VI, /* User Priority 4 */ |
| SME_QOS_EDCA_AC_VI, /* User Priority 5 */ |
| SME_QOS_EDCA_AC_VO, /* User Priority 6 */ |
| SME_QOS_EDCA_AC_VO /* User Priority 7 */ |
| }; |
| |
| /*--------------------------------------------------------------------------- |
| Table to map access category (AC) to appropriate user priority as specified |
| in 802.11e/WMM |
| Note: there is a quantization loss here because 4 ACs are mapped to 8 UPs |
| Mapping is done for consistency |
| ---------------------------------------------------------------------------*/ |
| sme_QosWmmUpType sme_QosACtoUPMap[SME_QOS_EDCA_AC_MAX] = |
| { |
| SME_QOS_WMM_UP_BE, /* AC BE */ |
| SME_QOS_WMM_UP_BK, /* AC BK */ |
| SME_QOS_WMM_UP_VI, /* AC VI */ |
| SME_QOS_WMM_UP_VO /* AC VO */ |
| }; |
| /*--------------------------------------------------------------------------- |
| DESCRIPTION |
| SME QoS module's FLOW Link List structure. This list can hold information per |
| flow/request, like TSPEC params requested, which AC it is running on |
| ---------------------------------------------------------------------------*/ |
| typedef struct sme_QosFlowInfoEntry_s |
| { |
| tListElem link; /* list links */ |
| v_U8_t sessionId; |
| v_U8_t tspec_mask; |
| sme_QosReasonType reason; |
| v_U32_t QosFlowID; |
| sme_QosEdcaAcType ac_type; |
| sme_QosWmmTspecInfo QoSInfo; |
| void * HDDcontext; |
| sme_QosCallback QoSCallback; |
| |
| /* |
| * Set to TRUE while re-negotiating flows after |
| * handoff, will set to FALSE once done with the process. Helps SME to |
| * decide if at all to notify HDD/LIS for flow renewal after HO |
| */ |
| v_BOOL_t hoRenewal; |
| } sme_QosFlowInfoEntry; |
| /*--------------------------------------------------------------------------- |
| DESCRIPTION |
| SME QoS module's setup request cmd related information structure. |
| ---------------------------------------------------------------------------*/ |
| typedef struct sme_QosSetupCmdInfo_s |
| { |
| v_U32_t QosFlowID; |
| sme_QosWmmTspecInfo QoSInfo; |
| void *HDDcontext; |
| sme_QosCallback QoSCallback; |
| sme_QosWmmUpType UPType; |
| |
| /* |
| * Set to TRUE while re-negotiating flows after |
| * handoff, will set to FALSE once done with the process. Helps SME to |
| * decide if at all to notify HDD/LIS for flow renewal after HO |
| */ |
| v_BOOL_t hoRenewal; |
| } sme_QosSetupCmdInfo; |
| /*--------------------------------------------------------------------------- |
| DESCRIPTION |
| SME QoS module's modify cmd related information structure. |
| ---------------------------------------------------------------------------*/ |
| typedef struct sme_QosModifyCmdInfo_s |
| { |
| v_U32_t QosFlowID; |
| sme_QosEdcaAcType ac; |
| sme_QosWmmTspecInfo QoSInfo; |
| } sme_QosModifyCmdInfo; |
| /*--------------------------------------------------------------------------- |
| DESCRIPTION |
| SME QoS module's resend cmd related information structure. |
| ---------------------------------------------------------------------------*/ |
| typedef struct sme_QosResendCmdInfo_s |
| { |
| v_U8_t tspecMask; |
| sme_QosEdcaAcType ac; |
| sme_QosWmmTspecInfo QoSInfo; |
| } sme_QosResendCmdInfo; |
| /*--------------------------------------------------------------------------- |
| DESCRIPTION |
| SME QoS module's release cmd related information structure. |
| ---------------------------------------------------------------------------*/ |
| typedef struct sme_QosReleaseCmdInfo_s |
| { |
| v_U32_t QosFlowID; |
| } sme_QosReleaseCmdInfo; |
| /*--------------------------------------------------------------------------- |
| DESCRIPTION |
| SME QoS module's buffered cmd related information structure. |
| ---------------------------------------------------------------------------*/ |
| typedef struct sme_QosCmdInfo_s |
| { |
| sme_QosCmdType command; |
| tpAniSirGlobal pMac; |
| v_U8_t sessionId; |
| union |
| { |
| sme_QosSetupCmdInfo setupCmdInfo; |
| sme_QosModifyCmdInfo modifyCmdInfo; |
| sme_QosResendCmdInfo resendCmdInfo; |
| sme_QosReleaseCmdInfo releaseCmdInfo; |
| }u; |
| } sme_QosCmdInfo; |
| /*--------------------------------------------------------------------------- |
| DESCRIPTION |
| SME QoS module's buffered cmd List structure. This list can hold information |
| related to any pending cmd from HDD |
| ---------------------------------------------------------------------------*/ |
| typedef struct sme_QosCmdInfoEntry_s |
| { |
| tListElem link; /* list links */ |
| sme_QosCmdInfo cmdInfo; |
| } sme_QosCmdInfoEntry; |
| /*--------------------------------------------------------------------------- |
| DESCRIPTION |
| SME QoS module's Per AC information structure. This can hold information on |
| how many flows running on the AC, the current, previous states the AC is in |
| ---------------------------------------------------------------------------*/ |
| typedef struct sme_QosACInfo_s |
| { |
| v_U8_t num_flows[SME_QOS_TSPEC_INDEX_MAX]; |
| sme_QosStates curr_state; |
| sme_QosStates prev_state; |
| sme_QosWmmTspecInfo curr_QoSInfo[SME_QOS_TSPEC_INDEX_MAX]; |
| sme_QosWmmTspecInfo requested_QoSInfo[SME_QOS_TSPEC_INDEX_MAX]; |
| v_BOOL_t reassoc_pending;//reassoc requested for APSD |
| //As per WMM spec there could be max 2 TSPEC running on the same AC with |
| //different direction. We will refer each TSPEC with an index |
| v_U8_t tspec_mask_status; //status showing if both the indices are in use |
| v_U8_t tspec_pending;//tspec negotiation going on for which index |
| |
| /* |
| * Set to TRUE while re-negotiating flows after |
| * handoff, will set to FALSE once done with the process. Helps SME to |
| * decide if at all to notify HDD/LIS for flow renewal after HO |
| */ |
| v_BOOL_t hoRenewal; |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| v_U8_t ricIdentifier[SME_QOS_TSPEC_INDEX_MAX]; |
| /* stores the ADD TS response for each AC. The ADD TS response is formed by |
| parsing the RIC received in the the reassoc response */ |
| tSirAddtsRsp addTsRsp[SME_QOS_TSPEC_INDEX_MAX]; |
| #endif |
| sme_QosRelTriggers relTrig; |
| |
| } sme_QosACInfo; |
| /*--------------------------------------------------------------------------- |
| DESCRIPTION |
| SME QoS module's Per session information structure. This can hold information |
| on the state of the session |
| ---------------------------------------------------------------------------*/ |
| typedef struct sme_QosSessionInfo_s |
| { |
| // what is this entry's session id |
| v_U8_t sessionId; |
| // is the session currently active |
| v_BOOL_t sessionActive; |
| // All AC info for this session |
| sme_QosACInfo ac_info[SME_QOS_EDCA_AC_MAX]; |
| // Bitmask of the ACs with APSD on |
| // Bit0:VO; Bit1:VI; Bit2:BK; Bit3:BE all other bits are ignored |
| v_U8_t apsdMask; |
| // association information for this session |
| sme_QosAssocInfo assocInfo; |
| // ID assigned to our reassoc request |
| v_U32_t roamID; |
| |
| /* |
| * Maintaining a power save status in QoS module, to be fed back to PMC at |
| * times through the sme_QosPmcCheckRoutine |
| */ |
| v_BOOL_t readyForPowerSave; |
| |
| // are we in the process of handing off to a different AP |
| v_BOOL_t handoffRequested; |
| // following reassoc or AddTS has UAPSD already been requested from PMC |
| v_BOOL_t uapsdAlreadyRequested; |
| // commands that are being buffered for this session |
| tDblLinkList bufferedCommandList; |
| |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| v_BOOL_t ftHandoffInProgress; |
| #endif |
| |
| } sme_QosSessionInfo; |
| /*--------------------------------------------------------------------------- |
| DESCRIPTION |
| Search key union. We can use the flowID, ac type, or reason to find an entry |
| in the flow list |
| ---------------------------------------------------------------------------*/ |
| typedef union sme_QosSearchKey_s |
| { |
| v_U32_t QosFlowID; |
| sme_QosEdcaAcType ac_type; |
| sme_QosReasonType reason; |
| }sme_QosSearchKey; |
| /*--------------------------------------------------------------------------- |
| DESCRIPTION |
| We can either use the flowID or the ac type to find an entry in the flow list. |
| The index is a bitmap telling us which key to use. Starting from LSB, |
| bit 0 - Flow ID |
| bit 1 - AC type |
| ---------------------------------------------------------------------------*/ |
| typedef struct sme_QosSearchInfo_s |
| { |
| v_U8_t sessionId; |
| v_U8_t index; |
| sme_QosSearchKey key; |
| sme_QosWmmDirType direction; |
| v_U8_t tspec_mask; |
| }sme_QosSearchInfo; |
| /*--------------------------------------------------------------------------- |
| DESCRIPTION |
| SME QoS module's internal control block. |
| ---------------------------------------------------------------------------*/ |
| struct sme_QosCb_s |
| { |
| //global Mac pointer |
| tpAniSirGlobal pMac; |
| //All Session Info |
| sme_QosSessionInfo sessionInfo[CSR_ROAM_SESSION_MAX]; |
| //All FLOW info |
| tDblLinkList flow_list; |
| //default TSPEC params |
| sme_QosWmmTspecInfo def_QoSInfo[SME_QOS_EDCA_AC_MAX]; |
| //counter for assigning Flow IDs |
| v_U32_t nextFlowId; |
| //counter for assigning Dialog Tokens |
| v_U8_t nextDialogToken; |
| }sme_QosCb; |
| typedef eHalStatus (*sme_QosProcessSearchEntry)(tpAniSirGlobal pMac, tListElem *pEntry); |
| /*-------------------------------------------------------------------------- |
| Internal function declarations |
| ------------------------------------------------------------------------*/ |
| sme_QosStatusType sme_QosInternalSetupReq(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| sme_QosWmmTspecInfo * pQoSInfo, |
| sme_QosCallback QoSCallback, |
| void * HDDcontext, |
| sme_QosWmmUpType UPType, |
| v_U32_t QosFlowID, |
| v_BOOL_t buffered_cmd, |
| v_BOOL_t hoRenewal); |
| sme_QosStatusType sme_QosInternalModifyReq(tpAniSirGlobal pMac, |
| sme_QosWmmTspecInfo * pQoSInfo, |
| v_U32_t QosFlowID, |
| v_BOOL_t buffered_cmd); |
| sme_QosStatusType sme_QosInternalReleaseReq(tpAniSirGlobal pMac, |
| uint8_t session_id, |
| v_U32_t QosFlowID, |
| v_BOOL_t buffered_cmd); |
| sme_QosStatusType sme_QosSetup(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| sme_QosWmmTspecInfo *pTspec_Info, |
| sme_QosEdcaAcType ac); |
| eHalStatus sme_QosAddTsReq(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| sme_QosWmmTspecInfo * pTspec_Info, |
| sme_QosEdcaAcType ac); |
| eHalStatus sme_QosDelTsReq(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| sme_QosEdcaAcType ac, |
| v_U8_t tspec_mask); |
| eHalStatus sme_QosProcessAddTsRsp(tpAniSirGlobal pMac, void *pMsgBuf); |
| eHalStatus sme_QosProcessDelTsInd(tpAniSirGlobal pMac, void *pMsgBuf); |
| eHalStatus sme_QosProcessDelTsRsp(tpAniSirGlobal pMac, void *pMsgBuf); |
| eHalStatus sme_QosProcessAssocCompleteEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info); |
| eHalStatus sme_QosProcessReassocReqEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info); |
| eHalStatus sme_QosProcessReassocSuccessEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info); |
| eHalStatus sme_QosProcessReassocFailureEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info); |
| eHalStatus sme_QosProcessDisconnectEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info); |
| eHalStatus sme_QosProcessJoinReqEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info); |
| eHalStatus sme_QosProcessHandoffAssocReqEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info); |
| eHalStatus sme_QosProcessHandoffSuccessEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info); |
| eHalStatus sme_QosProcessHandoffFailureEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info); |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| eHalStatus sme_QosProcessPreauthSuccessInd(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info); |
| eHalStatus sme_QosProcessSetKeySuccessInd(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info); |
| eHalStatus sme_QosProcessAggrQosRsp(tpAniSirGlobal pMac, void *pMsgBuf); |
| eHalStatus sme_QosFTAggrQosReq( tpAniSirGlobal pMac, v_U8_t sessionId ); |
| #endif |
| eHalStatus sme_QosProcessAddTsSuccessRsp(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| tSirAddtsRspInfo * pRsp); |
| eHalStatus sme_QosProcessAddTsFailureRsp(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| tSirAddtsRspInfo * pRsp); |
| eHalStatus sme_QosAggregateParams( |
| sme_QosWmmTspecInfo * pInput_Tspec_Info, |
| sme_QosWmmTspecInfo * pCurrent_Tspec_Info, |
| sme_QosWmmTspecInfo * pUpdated_Tspec_Info); |
| static eHalStatus sme_QosUpdateParams(v_U8_t sessionId, |
| sme_QosEdcaAcType ac, |
| v_U8_t tspec_mask, |
| sme_QosWmmTspecInfo * pTspec_Info); |
| sme_QosWmmUpType sme_QosAcToUp(sme_QosEdcaAcType ac); |
| sme_QosEdcaAcType sme_QosUpToAc(sme_QosWmmUpType up); |
| v_BOOL_t sme_QosIsACM(tpAniSirGlobal pMac, tSirBssDescription *pSirBssDesc, |
| sme_QosEdcaAcType ac, tDot11fBeaconIEs *pIes); |
| tListElem *sme_QosFindInFlowList(sme_QosSearchInfo search_key); |
| eHalStatus sme_QosFindAllInFlowList(tpAniSirGlobal pMac, |
| sme_QosSearchInfo search_key, |
| sme_QosProcessSearchEntry fnp); |
| static void sme_QosStateTransition(v_U8_t sessionId, |
| sme_QosEdcaAcType ac, |
| sme_QosStates new_state); |
| eHalStatus sme_QosBufferCmd(sme_QosCmdInfo *pcmd, v_BOOL_t insert_head); |
| static eHalStatus sme_QosProcessBufferedCmd(v_U8_t sessionId); |
| eHalStatus sme_QosSaveAssocInfo(sme_QosSessionInfo *pSession, sme_QosAssocInfo *pAssoc_info); |
| eHalStatus sme_QosSetupFnp(tpAniSirGlobal pMac, tListElem *pEntry); |
| eHalStatus sme_QosModificationNotifyFnp(tpAniSirGlobal pMac, tListElem *pEntry); |
| eHalStatus sme_QosModifyFnp(tpAniSirGlobal pMac, tListElem *pEntry); |
| eHalStatus sme_QosDelTsIndFnp(tpAniSirGlobal pMac, tListElem *pEntry); |
| eHalStatus sme_QosReassocSuccessEvFnp(tpAniSirGlobal pMac, tListElem *pEntry); |
| eHalStatus sme_QosAddTsFailureFnp(tpAniSirGlobal pMac, tListElem *pEntry); |
| eHalStatus sme_QosAddTsSuccessFnp(tpAniSirGlobal pMac, tListElem *pEntry); |
| static v_BOOL_t sme_QosIsRspPending(v_U8_t sessionId, sme_QosEdcaAcType ac); |
| static v_BOOL_t sme_QosIsUapsdActive(void); |
| void sme_QosPmcFullPowerCallback(void *callbackContext, eHalStatus status); |
| void sme_QosPmcOffloadFullPowerCallback(void *callbackContext, tANI_U32 sessionId, |
| eHalStatus status); |
| |
| void sme_QosPmcStartUapsdCallback(void *callbackContext, eHalStatus status); |
| void sme_QosPmcOffloadStartUapsdCallback(void *callbackContext, |
| tANI_U32 sessionId, eHalStatus status); |
| v_BOOL_t sme_QosPmcCheckRoutine(void *callbackContext); |
| v_BOOL_t sme_QosPmcOffloadCheckRoutine(void *callbackContext, tANI_U32 sessionId); |
| |
| void sme_QosPmcDeviceStateUpdateInd(void *callbackContext, tPmcState pmcState); |
| void sme_OffloadQosPmcDeviceStateUpdateInd(void *callbackContext, |
| tANI_U32 sessionId, tPmcState pmcState); |
| |
| eHalStatus sme_QosProcessOutOfUapsdMode(tpAniSirGlobal pMac); |
| eHalStatus sme_OffloadQosProcessOutOfUapsdMode(tpAniSirGlobal pMac, |
| tANI_U32 sessionId); |
| eHalStatus sme_QosProcessIntoUapsdMode(tpAniSirGlobal pMac); |
| eHalStatus sme_OffloadQosProcessIntoUapsdMode(tpAniSirGlobal pMac, |
| tANI_U32 sessionId); |
| static eHalStatus sme_QosBufferExistingFlows(tpAniSirGlobal pMac, |
| v_U8_t sessionId); |
| static eHalStatus sme_QosDeleteExistingFlows(tpAniSirGlobal pMac, |
| v_U8_t sessionId); |
| static void sme_QosCleanupCtrlBlkForHandoff(tpAniSirGlobal pMac, |
| v_U8_t sessionId); |
| static eHalStatus sme_QosDeleteBufferedRequests(tpAniSirGlobal pMac, |
| v_U8_t sessionId); |
| v_BOOL_t sme_QosValidateRequestedParams(tpAniSirGlobal pMac, |
| sme_QosWmmTspecInfo * pQoSInfo, |
| v_U8_t sessionId); |
| |
| extern eHalStatus sme_AcquireGlobalLock( tSmeStruct *psSme); |
| extern eHalStatus sme_ReleaseGlobalLock( tSmeStruct *psSme); |
| static eHalStatus qosIssueCommand( tpAniSirGlobal pMac, v_U8_t sessionId, |
| eSmeCommandType cmdType, sme_QosWmmTspecInfo * pQoSInfo, |
| sme_QosEdcaAcType ac, v_U8_t tspec_mask ); |
| /* |
| sme_QosReRequestAddTS to re-send AddTS for the combined QoS request |
| */ |
| static sme_QosStatusType sme_QosReRequestAddTS(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| sme_QosWmmTspecInfo * pQoSInfo, |
| sme_QosEdcaAcType ac, |
| v_U8_t tspecMask); |
| static void sme_QosInitACs(tpAniSirGlobal pMac, v_U8_t sessionId); |
| static eHalStatus sme_QosRequestReassoc(tpAniSirGlobal pMac, tANI_U8 sessionId, |
| tCsrRoamModifyProfileFields *pModFields, |
| v_BOOL_t fForce ); |
| static v_U32_t sme_QosAssignFlowId(void); |
| static v_U8_t sme_QosAssignDialogToken(void); |
| static eHalStatus sme_QosUpdateTspecMask(v_U8_t sessionId, |
| sme_QosSearchInfo search_key, |
| v_U8_t new_tspec_mask); |
| /*-------------------------------------------------------------------------- |
| External APIs definitions |
| ------------------------------------------------------------------------*/ |
| /* -------------------------------------------------------------------------- |
| \brief sme_QosOpen() - This function must be called before any API call to |
| SME QoS module. |
| \param pMac - Pointer to the global MAC parameter structure. |
| |
| \return eHalStatus |
| ----------------------------------------------------------------------------*/ |
| eHalStatus sme_QosOpen(tpAniSirGlobal pMac) |
| { |
| sme_QosSessionInfo *pSession; |
| v_U8_t sessionId; |
| eHalStatus status; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: initializing SME-QoS module", |
| __func__, __LINE__); |
| //init the control block |
| //(note that this will make all sessions invalid) |
| vos_mem_zero(&sme_QosCb, sizeof(sme_QosCb)); |
| sme_QosCb.pMac = pMac; |
| sme_QosCb.nextFlowId = SME_QOS_MIN_FLOW_ID; |
| sme_QosCb.nextDialogToken = SME_QOS_MIN_DIALOG_TOKEN; |
| //init flow list |
| status = csrLLOpen(pMac->hHdd, &sme_QosCb.flow_list); |
| if (!HAL_STATUS_SUCCESS(status)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, |
| "%s: %d: cannot initialize Flow List", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| for (sessionId = 0; sessionId < CSR_ROAM_SESSION_MAX; ++sessionId) |
| { |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| pSession->sessionId = sessionId; |
| // initialize the session's per-AC information |
| sme_QosInitACs(pMac, sessionId); |
| // initialize the session's buffered command list |
| status = csrLLOpen(pMac->hHdd, &pSession->bufferedCommandList); |
| if (!HAL_STATUS_SUCCESS(status)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, |
| "%s: %d: cannot initialize cmd list for session %d", |
| __func__, __LINE__, |
| sessionId); |
| return eHAL_STATUS_FAILURE; |
| } |
| pSession->readyForPowerSave = VOS_TRUE; |
| |
| if(pMac->psOffloadEnabled) |
| { |
| if(eHAL_STATUS_SUCCESS != pmcOffloadRegisterPowerSaveCheck(pMac, |
| sessionId, sme_QosPmcOffloadCheckRoutine, pMac)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, |
| "%s: %d: cannot register with pmcOffloadRegisterPowerSaveCheck()", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| if(eHAL_STATUS_SUCCESS != pmcOffloadRegisterDeviceStateUpdateInd(pMac, |
| sessionId, sme_OffloadQosPmcDeviceStateUpdateInd, pMac)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, |
| "%s: %d: cannot register with pmcOffloadRegisterPowerSaveCheck()", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| } |
| } |
| |
| if(!pMac->psOffloadEnabled) |
| { |
| /* |
| * The routine registered here gets called by PMC whenever the device |
| * is about to enter one of the power save modes. PMC runs a poll with all |
| * the registered modules if device can enter power save mode or |
| * remain full power. |
| */ |
| if(!HAL_STATUS_SUCCESS( |
| pmcRegisterPowerSaveCheck(pMac, sme_QosPmcCheckRoutine, pMac))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, |
| "%s: %d: cannot register with pmcRegisterPowerSaveCheck()", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| //the routine registered here gets called by PMC |
| //whenever there is a device |
| // state change. PMC might go to full power |
| //because of many reasons and this |
| // is the way for PMC to inform all the other |
| //registered modules so that |
| // everyone is in sync. |
| if(!HAL_STATUS_SUCCESS( |
| pmcRegisterDeviceStateUpdateInd(pMac, |
| sme_QosPmcDeviceStateUpdateInd, pMac))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, |
| "%s: %d: cannot register with pmcRegisterDeviceStateUpdateInd()", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: done initializing SME-QoS module", |
| __func__, __LINE__); |
| return eHAL_STATUS_SUCCESS; |
| } |
| /* -------------------------------------------------------------------------- |
| \brief sme_QosClose() - To close down SME QoS module. There should not be |
| any API call into this module after calling this function until another |
| call of sme_QosOpen. |
| \param pMac - Pointer to the global MAC parameter structure. |
| |
| \return eHalStatus |
| ----------------------------------------------------------------------------*/ |
| eHalStatus sme_QosClose(tpAniSirGlobal pMac) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosEdcaAcType ac; |
| v_U8_t sessionId; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: closing down SME-QoS", |
| __func__, __LINE__); |
| if(!pMac->psOffloadEnabled) |
| { |
| // deregister with PMC |
| if(!HAL_STATUS_SUCCESS( |
| pmcDeregisterDeviceStateUpdateInd(pMac, |
| sme_QosPmcDeviceStateUpdateInd))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, |
| "%s: %d: cannot deregister pmcDeregisterDeviceStateUpdateInd()", |
| __func__, __LINE__); |
| } |
| if(!HAL_STATUS_SUCCESS( |
| pmcDeregisterPowerSaveCheck(pMac, sme_QosPmcCheckRoutine))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, |
| "%s: %d: cannot deregister with pmcDeregisterPowerSaveCheck()", |
| __func__, __LINE__); |
| } |
| } |
| else |
| { |
| for(sessionId = 0; sessionId < CSR_ROAM_SESSION_MAX; ++sessionId) |
| { |
| /* deregister with PMC */ |
| if(!HAL_STATUS_SUCCESS( |
| pmcOffloadDeregisterDeviceStateUpdateInd(pMac, sessionId, |
| sme_OffloadQosPmcDeviceStateUpdateInd))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, |
| "%s: %d: cannot deregister DeviceStateUpdateInd()", |
| __func__, __LINE__); |
| } |
| if(!HAL_STATUS_SUCCESS( |
| pmcOffloadDeregisterPowerSaveCheck(pMac, sessionId, |
| sme_QosPmcOffloadCheckRoutine))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, |
| "%s: %d: cannot deregister with PowerSaveCheck()", |
| __func__, __LINE__); |
| } |
| } |
| } |
| |
| //cleanup control block |
| //close the flow list |
| csrLLClose(&sme_QosCb.flow_list); |
| // shut down all of the sessions |
| for(sessionId = 0; sessionId < CSR_ROAM_SESSION_MAX; ++sessionId) |
| { |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| if (pSession == NULL) |
| continue; |
| |
| sme_QosInitACs(pMac, sessionId); |
| // this session doesn't require UAPSD |
| pSession->apsdMask = 0; |
| |
| pSession->uapsdAlreadyRequested = VOS_FALSE; |
| pSession->handoffRequested = VOS_FALSE; |
| pSession->readyForPowerSave = VOS_TRUE; |
| pSession->roamID = 0; |
| //need to clean up buffered req |
| sme_QosDeleteBufferedRequests(pMac, sessionId); |
| //need to clean up flows |
| sme_QosDeleteExistingFlows(pMac, sessionId); |
| |
| // Clean up the assoc info if already allocated |
| if (pSession->assocInfo.pBssDesc) { |
| vos_mem_free(pSession->assocInfo.pBssDesc); |
| pSession->assocInfo.pBssDesc = NULL; |
| } |
| |
| // close the session's buffered command list |
| csrLLClose(&pSession->bufferedCommandList); |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| sme_QosStateTransition(sessionId, ac, SME_QOS_CLOSED); |
| } |
| pSession->sessionActive = VOS_FALSE; |
| pSession->readyForPowerSave = VOS_TRUE; |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: closed down QoS", |
| __func__, __LINE__); |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosSetupReq() - The SME QoS API exposed to HDD to request for QoS |
| on a particular AC. This function should be called after a link has been |
| established, i.e. STA is associated with an AP etc. If the request involves |
| admission control on the requested AC, HDD needs to provide the necessary |
| Traffic Specification (TSPEC) parameters otherwise SME is going to use the |
| default params. |
| |
| \param hHal - The handle returned by macOpen. |
| \param sessionId - sessionId returned by sme_OpenSession. |
| \param pQoSInfo - Pointer to sme_QosWmmTspecInfo which contains the WMM TSPEC |
| related info as defined above, provided by HDD |
| \param QoSCallback - The callback which is registered per flow while |
| requesting for QoS. Used for any notification for the |
| flow (i.e. setup success/failure/release) which needs to |
| be sent to HDD |
| \param HDDcontext - A cookie passed by HDD to be used by SME during any QoS |
| notification (through the callback) to HDD |
| \param UPType - Useful only if HDD or any other upper layer module (BAP etc.) |
| looking for implicit QoS setup, in that |
| case, the pQoSInfo will be NULL & SME will know about the AC |
| (from the UP provided in this param) QoS is requested on |
| \param pQosFlowID - Identification per flow running on each AC generated by |
| SME. |
| It is only meaningful if the QoS setup for the flow is |
| successful |
| |
| \return eHAL_STATUS_SUCCESS - Setup is successful. |
| |
| Other status means Setup request failed |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| sme_QosStatusType sme_QosSetupReq(tHalHandle hHal, tANI_U32 sessionId, |
| sme_QosWmmTspecInfo * pQoSInfo, |
| sme_QosCallback QoSCallback, |
| void * HDDcontext, |
| sme_QosWmmUpType UPType, v_U32_t * pQosFlowID) |
| { |
| sme_QosSessionInfo *pSession; |
| eHalStatus lock_status = eHAL_STATUS_FAILURE; |
| tpAniSirGlobal pMac = PMAC_STRUCT( hHal ); |
| sme_QosStatusType status; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: QoS Setup requested by client on session %d", |
| __func__, __LINE__, |
| sessionId); |
| lock_status = sme_AcquireGlobalLock( &pMac->sme ); |
| if ( !HAL_STATUS_SUCCESS( lock_status ) ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Unable to obtain lock", |
| __func__, __LINE__); |
| return SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| } |
| //Make sure the session is valid |
| if (!CSR_IS_SESSION_VALID( pMac, sessionId )) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Supplied Session ID %d is invalid", |
| __func__, __LINE__, |
| sessionId); |
| status = SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| } |
| else |
| { |
| //Make sure the session is active |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| if (!pSession->sessionActive) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Supplied Session ID %d is inactive", |
| __func__, __LINE__, |
| sessionId); |
| status = SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| } |
| else |
| { |
| //Assign a Flow ID |
| *pQosFlowID = sme_QosAssignFlowId(); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: QoS request on session %d assigned Flow ID %d", |
| __func__, __LINE__, |
| sessionId, *pQosFlowID); |
| //Call the internal function for QoS setup, |
| // adding a layer of abstraction |
| status = sme_QosInternalSetupReq(pMac, (v_U8_t)sessionId, pQoSInfo, |
| QoSCallback, HDDcontext, UPType, |
| *pQosFlowID, VOS_FALSE, VOS_FALSE); |
| } |
| } |
| sme_ReleaseGlobalLock( &pMac->sme ); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: QoS setup return status on session %d is %d", |
| __func__, __LINE__, |
| sessionId, status); |
| return status; |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosModifyReq() - The SME QoS API exposed to HDD to request for |
| modification of certain QoS params on a flow running on a particular AC. |
| This function should be called after a link has been established, i.e. STA is |
| associated with an AP etc. & a QoS setup has been successful for that flow. |
| If the request involves admission control on the requested AC, HDD needs to |
| provide the necessary Traffic Specification (TSPEC) parameters & SME might |
| start the renegotiation process through ADDTS. |
| |
| \param hHal - The handle returned by macOpen. |
| \param pQoSInfo - Pointer to sme_QosWmmTspecInfo which contains the WMM TSPEC |
| related info as defined above, provided by HDD |
| \param QosFlowID - Identification per flow running on each AC generated by |
| SME. |
| It is only meaningful if the QoS setup for the flow has |
| been successful already |
| |
| \return SME_QOS_STATUS_SETUP_SUCCESS_RSP - Modification is successful. |
| |
| Other status means request failed |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| sme_QosStatusType sme_QosModifyReq(tHalHandle hHal, |
| sme_QosWmmTspecInfo * pQoSInfo, |
| v_U32_t QosFlowID) |
| { |
| eHalStatus lock_status = eHAL_STATUS_FAILURE; |
| tpAniSirGlobal pMac = PMAC_STRUCT( hHal ); |
| sme_QosStatusType status; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: QoS Modify requested by client for Flow %d", |
| __func__, __LINE__, |
| QosFlowID); |
| lock_status = sme_AcquireGlobalLock( &pMac->sme ); |
| if ( !HAL_STATUS_SUCCESS( lock_status ) ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Unable to obtain lock", |
| __func__, __LINE__); |
| return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| } |
| //Call the internal function for QoS modify, adding a layer of abstraction |
| status = sme_QosInternalModifyReq(pMac, pQoSInfo, QosFlowID, VOS_FALSE); |
| sme_ReleaseGlobalLock( &pMac->sme ); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: QoS Modify return status on Flow %d is %d", |
| __func__, __LINE__, |
| QosFlowID, status); |
| return status; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosReleaseReq() - The SME QoS API exposed to HDD to request for |
| releasing a QoS flow running on a particular AC. This function should be |
| called only if a QoS is set up with a valid FlowID. HDD should invoke this |
| API only if an explicit request for QoS release has come from Application |
| |
| \param hHal - The handle returned by macOpen. |
| \param session_id - session_id returned by sme_OpenSession. |
| \param QosFlowID - Identification per flow running on each AC generated by SME |
| It is only meaningful if the QoS setup for the flow is |
| successful |
| |
| \return eHAL_STATUS_SUCCESS - Release is successful. |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| sme_QosStatusType sme_QosReleaseReq(tHalHandle hHal, uint8_t session_id, |
| v_U32_t QosFlowID) |
| { |
| eHalStatus lock_status = eHAL_STATUS_FAILURE; |
| tpAniSirGlobal pMac = PMAC_STRUCT( hHal ); |
| sme_QosStatusType status; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: QoS Release requested by client for Flow %d", |
| __func__, __LINE__, |
| QosFlowID); |
| lock_status = sme_AcquireGlobalLock( &pMac->sme ); |
| if ( !HAL_STATUS_SUCCESS( lock_status ) ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Unable to obtain lock", |
| __func__, __LINE__); |
| return SME_QOS_STATUS_RELEASE_FAILURE_RSP; |
| } |
| //Call the internal function for QoS release, adding a layer of abstraction |
| status = sme_QosInternalReleaseReq(pMac, session_id, QosFlowID, VOS_FALSE); |
| sme_ReleaseGlobalLock( &pMac->sme ); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: QoS Release return status on Flow %d is %d", |
| __func__, __LINE__, |
| QosFlowID, status); |
| return status; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosSetParams() - This function is used by HDD to provide the |
| default TSPEC params to SME. |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pQoSInfo - Pointer to sme_QosWmmTspecInfo which contains the WMM TSPEC |
| related info per AC as defined above, provided by HDD |
| |
| \return eHAL_STATUS_SUCCESS - Setparam is successful. |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosSetParams(tpAniSirGlobal pMac, sme_QosWmmTspecInfo * pQoSInfo) |
| { |
| sme_QosEdcaAcType ac; |
| // find the AC |
| ac = sme_QosUpToAc(pQoSInfo->ts_info.up); |
| if(SME_QOS_EDCA_AC_MAX == ac) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Invalid AC %d (via UP %d)", |
| __func__, __LINE__, |
| ac, pQoSInfo->ts_info.up ); |
| return eHAL_STATUS_FAILURE; |
| } |
| //copy over the default params for this AC |
| sme_QosCb.def_QoSInfo[ac] = *pQoSInfo; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: QoS default params set for AC %d (via UP %d)", |
| __func__, __LINE__, |
| ac, pQoSInfo->ts_info.up ); |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| void qosReleaseCommand( tpAniSirGlobal pMac, tSmeCmd *pCommand ) |
| { |
| vos_mem_zero( &pCommand->u.qosCmd, sizeof( tGenericQosCmd ) ); |
| smeReleaseCommand( pMac, pCommand ); |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosMsgProcessor() - sme_ProcessMsg() calls this function for the |
| messages that are handled by SME QoS module. |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param msg_type - the type of msg passed by PE as defined in wniApi.h |
| \param pMsgBuf - a pointer to a buffer that maps to various structures base |
| on the message type. |
| The beginning of the buffer can always map to tSirSmeRsp. |
| |
| \return eHAL_STATUS_SUCCESS - Validation is successful. |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosMsgProcessor( tpAniSirGlobal pMac, v_U16_t msg_type, |
| void *pMsgBuf) |
| { |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| tListElem *pEntry; |
| tSmeCmd *pCommand; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: msg = %d for QoS", |
| __func__, __LINE__, msg_type); |
| //switch on the msg type & make the state transition accordingly |
| switch(msg_type) |
| { |
| case eWNI_SME_ADDTS_RSP: |
| pEntry = csrLLPeekHead(&pMac->sme.smeCmdActiveList, LL_ACCESS_LOCK); |
| if( pEntry ) |
| { |
| pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link); |
| if( eSmeCommandAddTs == pCommand->command ) |
| { |
| status = sme_QosProcessAddTsRsp(pMac, pMsgBuf); |
| if( csrLLRemoveEntry( &pMac->sme.smeCmdActiveList, pEntry, LL_ACCESS_LOCK ) ) |
| { |
| qosReleaseCommand( pMac, pCommand ); |
| } |
| smeProcessPendingQueue( pMac ); |
| } |
| } |
| break; |
| case eWNI_SME_DELTS_RSP: |
| pEntry = csrLLPeekHead(&pMac->sme.smeCmdActiveList, LL_ACCESS_LOCK); |
| if( pEntry ) |
| { |
| pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link); |
| if( eSmeCommandDelTs == pCommand->command ) |
| { |
| status = sme_QosProcessDelTsRsp(pMac, pMsgBuf); |
| if( csrLLRemoveEntry( &pMac->sme.smeCmdActiveList, pEntry, LL_ACCESS_LOCK ) ) |
| { |
| qosReleaseCommand( pMac, pCommand ); |
| } |
| smeProcessPendingQueue( pMac ); |
| } |
| } |
| break; |
| case eWNI_SME_DELTS_IND: |
| status = sme_QosProcessDelTsInd(pMac, pMsgBuf); |
| break; |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| case eWNI_SME_FT_AGGR_QOS_RSP: |
| status = sme_QosProcessAggrQosRsp(pMac, pMsgBuf); |
| break; |
| #endif |
| |
| default: |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: unknown msg type = %d", |
| __func__, __LINE__, msg_type); |
| break; |
| } |
| return status; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosValidateParams() - The SME QoS API exposed to CSR to validate AP |
| capabilities regarding QoS support & any other QoS parameter validation. |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pBssDesc - Pointer to the BSS Descriptor information passed down by |
| CSR to PE while issuing the Join request |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosValidateParams(tpAniSirGlobal pMac, |
| tSirBssDescription *pBssDesc) |
| { |
| tDot11fBeaconIEs *pIes = NULL; |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: validation for QAP & APSD", |
| __func__, __LINE__); |
| do |
| { |
| if(!HAL_STATUS_SUCCESS(csrGetParsedBssDescriptionIEs(pMac, pBssDesc, &pIes))) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: csrGetParsedBssDescriptionIEs() failed", |
| __func__, __LINE__); |
| break; |
| } |
| //check if the AP is QAP & it supports APSD |
| if( !CSR_IS_QOS_BSS(pIes) ) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: AP doesn't support QoS", |
| __func__, __LINE__); |
| |
| break; |
| } |
| if(!(pIes->WMMParams.qosInfo & SME_QOS_AP_SUPPORTS_APSD) && |
| !(pIes->WMMInfoAp.uapsd)) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: AP doesn't support APSD", |
| __func__, __LINE__); |
| break; |
| } |
| status = eHAL_STATUS_SUCCESS; |
| }while(0); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: validated with status = %d", |
| __func__, __LINE__, status); |
| if(pIes) |
| { |
| vos_mem_free(pIes); |
| } |
| return status; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosCsrEventInd() - The QoS sub-module in SME expects notifications |
| from CSR when certain events occur as mentioned in sme_QosCsrEventIndType. |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param ind - The event occurred of type sme_QosCsrEventIndType. |
| \param pEvent_info - Information related to the event |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosCsrEventInd(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| sme_QosCsrEventIndType ind, |
| void *pEvent_info) |
| { |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On Session %d Event %d received from CSR", |
| __func__, __LINE__, |
| sessionId, ind ); |
| switch(ind) |
| { |
| case SME_QOS_CSR_ASSOC_COMPLETE: |
| //expecting assoc info in pEvent_info |
| status = sme_QosProcessAssocCompleteEv(pMac, sessionId, pEvent_info); |
| break; |
| case SME_QOS_CSR_REASSOC_REQ: |
| //nothing expected in pEvent_info |
| status = sme_QosProcessReassocReqEv(pMac, sessionId, pEvent_info); |
| break; |
| case SME_QOS_CSR_REASSOC_COMPLETE: |
| //expecting assoc info in pEvent_info |
| status = sme_QosProcessReassocSuccessEv(pMac, sessionId, pEvent_info); |
| break; |
| case SME_QOS_CSR_REASSOC_FAILURE: |
| //nothing expected in pEvent_info |
| status = sme_QosProcessReassocFailureEv(pMac, sessionId, pEvent_info); |
| break; |
| case SME_QOS_CSR_DISCONNECT_REQ: |
| case SME_QOS_CSR_DISCONNECT_IND: |
| //nothing expected in pEvent_info |
| status = sme_QosProcessDisconnectEv(pMac, sessionId, pEvent_info); |
| break; |
| case SME_QOS_CSR_JOIN_REQ: |
| //nothing expected in pEvent_info |
| status = sme_QosProcessJoinReqEv(pMac, sessionId, pEvent_info); |
| break; |
| case SME_QOS_CSR_HANDOFF_ASSOC_REQ: |
| //nothing expected in pEvent_info |
| status = sme_QosProcessHandoffAssocReqEv(pMac, sessionId, pEvent_info); |
| break; |
| case SME_QOS_CSR_HANDOFF_COMPLETE: |
| //nothing expected in pEvent_info |
| status = sme_QosProcessHandoffSuccessEv(pMac, sessionId, pEvent_info); |
| break; |
| case SME_QOS_CSR_HANDOFF_FAILURE: |
| //nothing expected in pEvent_info |
| status = sme_QosProcessHandoffFailureEv(pMac, sessionId, pEvent_info); |
| break; |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| case SME_QOS_CSR_PREAUTH_SUCCESS_IND: |
| status = sme_QosProcessPreauthSuccessInd(pMac, sessionId, pEvent_info); |
| break; |
| #if defined(FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR) |
| case SME_QOS_CSR_SET_KEY_SUCCESS_IND: |
| status = sme_QosProcessSetKeySuccessInd(pMac, sessionId, pEvent_info); |
| break; |
| #endif |
| #endif |
| default: |
| //Err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On Session %d Unknown Event %d received from CSR", |
| __func__, __LINE__, |
| sessionId, ind ); |
| break; |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On Session %d processed Event %d with status %d", |
| __func__, __LINE__, |
| sessionId, ind, status ); |
| return status; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosGetACMMask() - The QoS sub-module API to find out on which ACs |
| AP mandates Admission Control (ACM = 1) |
| (Bit0:VO; Bit1:VI; Bit2:BK; Bit3:BE all other bits are ignored) |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pSirBssDesc - The event occurred of type sme_QosCsrEventIndType. |
| |
| \return a bit mask indicating for which ACs AP has ACM set to 1 |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| v_U8_t sme_QosGetACMMask(tpAniSirGlobal pMac, tSirBssDescription *pSirBssDesc, tDot11fBeaconIEs *pIes) |
| { |
| sme_QosEdcaAcType ac; |
| v_U8_t acm_mask = 0; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked", |
| __func__, __LINE__); |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| if(sme_QosIsACM(pMac, pSirBssDesc, ac, pIes)) |
| { |
| acm_mask = acm_mask | (1 << (SME_QOS_EDCA_AC_VO - ac)); |
| } |
| |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: mask is %d", |
| __func__, __LINE__, acm_mask); |
| return acm_mask; |
| } |
| /*-------------------------------------------------------------------------- |
| Internal function definitions |
| ------------------------------------------------------------------------*/ |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosInternalSetupReq() - The SME QoS internal setup request handling |
| function. |
| If the request involves admission control on the requested AC, HDD needs to |
| provide the necessary Traffic Specification (TSPEC) parameters otherwise SME |
| is going to use the default params. |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pQoSInfo - Pointer to sme_QosWmmTspecInfo which contains the WMM TSPEC |
| related info as defined above, provided by HDD |
| \param QoSCallback - The callback which is registered per flow while |
| requesting for QoS. Used for any notification for the |
| flow (i.e. setup success/failure/release) which needs to |
| be sent to HDD |
| \param HDDcontext - A cookie passed by HDD to be used by SME during any QoS |
| notification (through the callback) to HDD |
| \param UPType - Useful only if HDD or any other upper layer module (BAP etc.) |
| looking for implicit QoS setup, in that |
| case, the pQoSInfo will be NULL & SME will know about the AC |
| (from the UP provided in this param) QoS is requested on |
| \param QosFlowID - Identification per flow running on each AC generated by |
| SME. |
| It is only meaningful if the QoS setup for the flow is |
| successful |
| \param buffered_cmd - tells us if the cmd was a buffered one or fresh from |
| client |
| |
| \return eHAL_STATUS_SUCCESS - Setup is successful. |
| |
| Other status means Setup request failed |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| sme_QosStatusType sme_QosInternalSetupReq(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| sme_QosWmmTspecInfo * pQoSInfo, |
| sme_QosCallback QoSCallback, |
| void * HDDcontext, |
| sme_QosWmmUpType UPType, |
| v_U32_t QosFlowID, |
| v_BOOL_t buffered_cmd, |
| v_BOOL_t hoRenewal) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosEdcaAcType ac; |
| sme_QosWmmTspecInfo Tspec_Info; |
| sme_QosStates new_state = SME_QOS_CLOSED; |
| sme_QosFlowInfoEntry *pentry = NULL; |
| sme_QosCmdInfo cmd; |
| sme_QosStatusType status = SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| v_U8_t tmask = 0; |
| v_U8_t new_tmask = 0; |
| sme_QosSearchInfo search_key; |
| v_BOOL_t bufferCommand; |
| eHalStatus hstatus; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d for flow %d", |
| __func__, __LINE__, |
| sessionId, QosFlowID); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| // if caller sent an empty TSPEC, fill up with the default one |
| if(!pQoSInfo) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN, |
| "%s: %d: caller sent an empty QoS param list, using defaults", |
| __func__, __LINE__); |
| // find the AC with UPType passed in |
| ac = sme_QosUpToAc(UPType); |
| if(SME_QOS_EDCA_AC_MAX == ac) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: invalid AC %d from UP %d", |
| __func__, __LINE__, |
| ac, UPType); |
| |
| return SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP; |
| } |
| Tspec_Info = sme_QosCb.def_QoSInfo[ac]; |
| } |
| else |
| { |
| // find the AC |
| ac = sme_QosUpToAc(pQoSInfo->ts_info.up); |
| if(SME_QOS_EDCA_AC_MAX == ac) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: invalid AC %d from UP %d", |
| __func__, __LINE__, |
| ac, pQoSInfo->ts_info.up); |
| |
| return SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP; |
| } |
| //validate QoS params |
| if(!sme_QosValidateRequestedParams(pMac, pQoSInfo, sessionId)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: invalid params", |
| __func__, __LINE__); |
| return SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP; |
| } |
| Tspec_Info = *pQoSInfo; |
| } |
| pACInfo = &pSession->ac_info[ac]; |
| /* Need to vote off power save for the duration of this request */ |
| pSession->readyForPowerSave = VOS_FALSE; |
| // assume we won't have to (re)buffer the command |
| bufferCommand = VOS_FALSE; |
| |
| /* |
| * Check to consider the following flowing scenario Addts request is pending |
| * on one AC, while APSD requested on another which needs a reassoc. |
| * Will buffer a request if Addts is pending on any AC, which will safeguard |
| * the above scenario, & also won't confuse PE with back to back Addts or |
| * Addts followed by Reassoc. |
| */ |
| if(sme_QosIsRspPending(sessionId, ac)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: buffering the setup request for flow %d in state %d " |
| "since another request is pending", |
| __func__, __LINE__, |
| QosFlowID, pACInfo->curr_state ); |
| bufferCommand = VOS_TRUE; |
| } |
| else |
| { |
| // make sure we are in full power so that we can issue |
| // an AddTS or reassoc if necessary |
| if(!pMac->psOffloadEnabled) |
| { |
| hstatus = pmcRequestFullPower(pMac, sme_QosPmcFullPowerCallback, |
| pSession, eSME_REASON_OTHER); |
| } |
| else |
| { |
| hstatus = pmcOffloadRequestFullPower(pMac, sessionId, |
| sme_QosPmcOffloadFullPowerCallback, |
| pSession, eSME_REASON_OTHER); |
| } |
| |
| if( eHAL_STATUS_PMC_PENDING == hstatus ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: buffering the setup request for flow %d in state %d, " |
| "waiting for full power", |
| __func__, __LINE__, |
| QosFlowID, pACInfo->curr_state ); |
| bufferCommand = VOS_TRUE; |
| } |
| } |
| if (bufferCommand) |
| { |
| // we need to buffer the command |
| cmd.command = SME_QOS_SETUP_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.setupCmdInfo.HDDcontext = HDDcontext; |
| cmd.u.setupCmdInfo.QoSInfo = Tspec_Info; |
| cmd.u.setupCmdInfo.QoSCallback = QoSCallback; |
| cmd.u.setupCmdInfo.UPType = UPType; |
| cmd.u.setupCmdInfo.hoRenewal = hoRenewal; |
| cmd.u.setupCmdInfo.QosFlowID = QosFlowID; |
| hstatus = sme_QosBufferCmd(&cmd, buffered_cmd); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't buffer the setup request in state = %d", |
| __func__, __LINE__, |
| pACInfo->curr_state ); |
| /* Unable to buffer the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Buffered setup request for flow = %d", |
| __func__, __LINE__, |
| QosFlowID); |
| return SME_QOS_STATUS_SETUP_REQ_PENDING_RSP; |
| } |
| |
| //get into the state m/c to see if the request can be granted |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_LINK_UP: |
| //call the internal qos setup logic to decide on if the |
| // request is NOP, or need reassoc for APSD and/or need to send out ADDTS |
| status = sme_QosSetup(pMac, sessionId, &Tspec_Info, ac); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d with AC %d in state SME_QOS_LINK_UP " |
| "sme_QosSetup returned with status %d", |
| __func__, __LINE__, |
| sessionId, ac, status); |
| if(SME_QOS_STATUS_SETUP_REQ_PENDING_RSP != status) |
| { |
| /* We aren't waiting for a response from the AP |
| so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| } |
| if((SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status)|| |
| (SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) || |
| (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status)) |
| { |
| // we received an expected "good" status |
| //create an entry in the flow list |
| pentry = vos_mem_malloc(sizeof(*pentry)); |
| if (!pentry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't allocate memory for the new " |
| "entry in the Flow List", |
| __func__, __LINE__); |
| return SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| } |
| pentry->ac_type = ac; |
| pentry->HDDcontext = HDDcontext; |
| pentry->QoSCallback = QoSCallback; |
| pentry->hoRenewal = hoRenewal; |
| pentry->QosFlowID = QosFlowID; |
| pentry->sessionId = sessionId; |
| // since we are in state SME_QOS_LINK_UP this must be the |
| // first TSPEC on this AC, so use index 0 (mask bit 1) |
| pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0] = Tspec_Info; |
| if(SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status) |
| { |
| if(pACInfo->tspec_mask_status && |
| !pACInfo->reassoc_pending) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d with AC %d in state " |
| "SME_QOS_LINK_UP tspec_mask_status is %d " |
| "but should not be set yet", |
| __func__, __LINE__, |
| sessionId, ac, pACInfo->tspec_mask_status); |
| VOS_ASSERT(0); |
| vos_mem_free(pentry); |
| return SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| } |
| pACInfo->tspec_mask_status = SME_QOS_TSPEC_MASK_BIT_1_SET; |
| if(!pACInfo->reassoc_pending) |
| { |
| // we didn't request for reassoc, it must be a tspec negotiation |
| pACInfo->tspec_pending = 1; |
| } |
| |
| pentry->reason = SME_QOS_REASON_SETUP; |
| new_state = SME_QOS_REQUESTED; |
| } |
| else |
| { |
| // SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP or |
| // SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY |
| pentry->reason = SME_QOS_REASON_REQ_SUCCESS; |
| new_state = SME_QOS_QOS_ON; |
| pACInfo->tspec_mask_status = SME_QOS_TSPEC_MASK_BIT_1_SET; |
| pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0] = Tspec_Info; |
| if(buffered_cmd && !pentry->hoRenewal) |
| { |
| QoSCallback(pMac, HDDcontext, |
| &pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0], |
| status, |
| pentry->QosFlowID); |
| } |
| pentry->hoRenewal = VOS_FALSE; |
| } |
| pACInfo->num_flows[SME_QOS_TSPEC_INDEX_0]++; |
| |
| //indicate on which index the flow entry belongs to & add it to the |
| //Flow List at the end |
| pentry->tspec_mask = pACInfo->tspec_mask_status; |
| pentry->QoSInfo = Tspec_Info; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Creating entry on session %d at %pK with flowID %d", |
| __func__, __LINE__, |
| sessionId, pentry, QosFlowID); |
| csrLLInsertTail(&sme_QosCb.flow_list, &pentry->link, VOS_TRUE); |
| } |
| else |
| { |
| // unexpected status returned by sme_QosSetup() |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d unexpected status %d " |
| "returned by sme_QosSetup", |
| __func__, __LINE__, |
| sessionId, status); |
| new_state = pACInfo->curr_state; |
| if(buffered_cmd && hoRenewal) |
| { |
| QoSCallback(pMac, HDDcontext, |
| &pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0], |
| SME_QOS_STATUS_RELEASE_QOS_LOST_IND, |
| QosFlowID); |
| } |
| } |
| break; |
| case SME_QOS_HANDOFF: |
| case SME_QOS_REQUESTED: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: Buffering setup request for flow %d in state = %d", |
| __func__, __LINE__, |
| QosFlowID, pACInfo->curr_state ); |
| //buffer cmd |
| cmd.command = SME_QOS_SETUP_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.setupCmdInfo.HDDcontext = HDDcontext; |
| cmd.u.setupCmdInfo.QoSInfo = Tspec_Info; |
| cmd.u.setupCmdInfo.QoSCallback = QoSCallback; |
| cmd.u.setupCmdInfo.UPType = UPType; |
| cmd.u.setupCmdInfo.hoRenewal = hoRenewal; |
| cmd.u.setupCmdInfo.QosFlowID = QosFlowID; |
| hstatus = sme_QosBufferCmd(&cmd, buffered_cmd); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d couldn't buffer the setup " |
| "request for flow %d in state = %d", |
| __func__, __LINE__, |
| sessionId, QosFlowID, pACInfo->curr_state ); |
| /* Unable to buffer the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| } |
| status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP; |
| new_state = pACInfo->curr_state; |
| break; |
| case SME_QOS_QOS_ON: |
| |
| //check if multiple flows running on the ac |
| if((pACInfo->num_flows[SME_QOS_TSPEC_INDEX_0] > 0)|| |
| (pACInfo->num_flows[SME_QOS_TSPEC_INDEX_1] > 0)) |
| { |
| //do we need to care about the case where APSD needed on ACM = 0 below? |
| if(CSR_IS_ADDTS_WHEN_ACMOFF_SUPPORTED(pMac) || |
| sme_QosIsACM(pMac, pSession->assocInfo.pBssDesc, ac, NULL)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: tspec_mask_status = %d for AC = %d", |
| __func__, __LINE__, |
| pACInfo->tspec_mask_status, ac); |
| if(!pACInfo->tspec_mask_status) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: tspec_mask_status can't be 0 for ac = %d in " |
| "state = %d", |
| __func__, __LINE__, |
| ac, pACInfo->curr_state); |
| VOS_ASSERT(0); |
| /* Unable to buffer the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return status; |
| } |
| /* Flow aggregation */ |
| if ( ((pACInfo->tspec_mask_status > 0) && |
| (pACInfo->tspec_mask_status <= SME_QOS_TSPEC_INDEX_MAX)) ) |
| { |
| /* |
| * Either of upstream, downstream or bidirectional |
| * flows are present. If either of new stream or current stream is |
| * for bidirectional, aggregate the new stream with the current |
| * streams present and send out aggregated Tspec. |
| */ |
| if((Tspec_Info.ts_info.direction == SME_QOS_WMM_TS_DIR_BOTH) || |
| (pACInfo->curr_QoSInfo[pACInfo->tspec_mask_status - 1]. |
| ts_info.direction == SME_QOS_WMM_TS_DIR_BOTH)) |
| { |
| // Aggregate the new stream with the current stream(s). |
| tmask = pACInfo->tspec_mask_status; |
| } |
| /* None of new stream or current (aggregated) streams are for bidirectional. |
| * Check if the new stream direction matches the current stream direction. */ |
| else if(pACInfo->curr_QoSInfo[pACInfo->tspec_mask_status - 1]. |
| ts_info.direction == Tspec_Info.ts_info.direction) |
| { |
| // Aggregate the new stream with the current stream(s). |
| tmask = pACInfo->tspec_mask_status; |
| } |
| /* New stream is in different direction. */ |
| else |
| { |
| // No Aggregation. Mark the 2nd tpsec index also as active. |
| tmask = SME_QOS_TSPEC_MASK_CLEAR; |
| new_tmask = SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~pACInfo->tspec_mask_status; |
| pACInfo->tspec_mask_status = SME_QOS_TSPEC_MASK_BIT_1_2_SET; |
| } |
| } |
| else if(SME_QOS_TSPEC_MASK_BIT_1_2_SET == pACInfo->tspec_mask_status) |
| { |
| /* Both uplink and downlink streams are present. */ |
| /* If new stream is bidirectional, aggregate new stream with all existing |
| * upstreams and downstreams. Send out new aggregated tpsec. */ |
| if(Tspec_Info.ts_info.direction == SME_QOS_WMM_TS_DIR_BOTH) |
| { |
| // Only one tspec index (0) will be in use after this aggregation. |
| tmask = SME_QOS_TSPEC_MASK_BIT_1_2_SET; |
| pACInfo->tspec_mask_status = SME_QOS_TSPEC_MASK_BIT_1_SET; |
| } |
| /* New stream is also uni-directional |
| * Find out the tsepc index with which it needs to be aggregated */ |
| else if(pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0].ts_info.direction != |
| Tspec_Info.ts_info.direction) |
| { |
| // Aggregate with 2nd tspec index |
| tmask = SME_QOS_TSPEC_MASK_BIT_2_SET; |
| } |
| else |
| { |
| // Aggregate with 1st tspec index |
| tmask = SME_QOS_TSPEC_MASK_BIT_1_SET; |
| } |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: wrong tmask = %d", __func__, __LINE__, |
| pACInfo->tspec_mask_status ); |
| } |
| } |
| else |
| { |
| //ACM = 0 |
| // We won't be sending a TSPEC to the AP but we still need |
| // to aggregate to calculate trigger frame parameters |
| tmask = SME_QOS_TSPEC_MASK_BIT_1_SET; |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: tmask = %d, new_tmask = %d in state = %d", |
| __func__, __LINE__, |
| tmask, new_tmask, pACInfo->curr_state ); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: tspec_mask_status = %d for AC = %d", |
| __func__, __LINE__, |
| pACInfo->tspec_mask_status, ac); |
| if(tmask) |
| { |
| // create the aggregate TSPEC |
| if(tmask != SME_QOS_TSPEC_MASK_BIT_1_2_SET) |
| { |
| hstatus = sme_QosAggregateParams(&Tspec_Info, |
| &pACInfo->curr_QoSInfo[tmask - 1], |
| &pACInfo->requested_QoSInfo[tmask - 1]); |
| } |
| else |
| { |
| /* Aggregate the new bidirectional stream with the existing upstreams and |
| * downstreams in tspec indices 0 and 1. */ |
| tmask = SME_QOS_TSPEC_MASK_BIT_1_SET; |
| |
| if((hstatus = sme_QosAggregateParams(&Tspec_Info, |
| &pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0], |
| &pACInfo->requested_QoSInfo[tmask - 1])) |
| == eHAL_STATUS_SUCCESS) |
| { |
| hstatus = sme_QosAggregateParams(&pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_1], |
| &pACInfo->requested_QoSInfo[tmask - 1], |
| NULL); |
| } |
| } |
| |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: failed to aggregate params", |
| __func__, __LINE__); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| } |
| } |
| else |
| { |
| if (!(new_tmask > 0 && new_tmask <= SME_QOS_TSPEC_INDEX_MAX)) |
| { |
| return SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| } |
| tmask = new_tmask; |
| pACInfo->requested_QoSInfo[tmask-1] = Tspec_Info; |
| } |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: no flows running for ac = %d while in state = %d", |
| __func__, __LINE__, |
| ac, pACInfo->curr_state ); |
| VOS_ASSERT(0); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return status; |
| } |
| //although aggregating, make sure to request on the correct UP,TID,PSB and direction |
| pACInfo->requested_QoSInfo[tmask - 1].ts_info.up = Tspec_Info.ts_info.up; |
| pACInfo->requested_QoSInfo[tmask - 1].ts_info.tid = Tspec_Info.ts_info.tid; |
| pACInfo->requested_QoSInfo[tmask - 1].ts_info.direction = Tspec_Info.ts_info.direction; |
| pACInfo->requested_QoSInfo[tmask - 1].ts_info.psb = Tspec_Info.ts_info.psb; |
| status = sme_QosSetup(pMac, sessionId, |
| &pACInfo->requested_QoSInfo[tmask - 1], ac); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d with AC %d in state SME_QOS_QOS_ON " |
| "sme_QosSetup returned with status %d", |
| __func__, __LINE__, |
| sessionId, ac, status); |
| if(SME_QOS_STATUS_SETUP_REQ_PENDING_RSP != status) |
| { |
| /* We aren't waiting for a response from the AP |
| so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| } |
| if((SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status)|| |
| (SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) || |
| (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status)) |
| { |
| // we received an expected "good" status |
| //create an entry in the flow list |
| pentry = (sme_QosFlowInfoEntry *) vos_mem_malloc(sizeof(*pentry)); |
| if (!pentry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't allocate memory for the new " |
| "entry in the Flow List", |
| __func__, __LINE__); |
| return SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| } |
| pentry->ac_type = ac; |
| pentry->HDDcontext = HDDcontext; |
| pentry->QoSCallback = QoSCallback; |
| pentry->hoRenewal = hoRenewal; |
| pentry->QosFlowID = QosFlowID; |
| pentry->sessionId = sessionId; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Creating flow %d", |
| __func__, __LINE__, |
| QosFlowID); |
| if((SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status)|| |
| (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status)) |
| { |
| new_state = pACInfo->curr_state; |
| pentry->reason = SME_QOS_REASON_REQ_SUCCESS; |
| pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0] = |
| pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0]; |
| if(buffered_cmd && !pentry->hoRenewal) |
| { |
| QoSCallback(pMac, HDDcontext, |
| &pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0], |
| status, |
| pentry->QosFlowID); |
| } |
| if(SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status) |
| { |
| // if we are not in handoff, then notify all flows on |
| // this AC that the aggregate TSPEC may have changed |
| if(!pentry->hoRenewal) |
| { |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| search_key.key.ac_type = ac; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; |
| search_key.sessionId = sessionId; |
| hstatus = sme_QosFindAllInFlowList(pMac, search_key, |
| sme_QosSetupFnp); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't notify other " |
| "entries on this AC =%d", |
| __func__, __LINE__, ac); |
| } |
| } |
| } |
| pentry->hoRenewal = VOS_FALSE; |
| } |
| else |
| { |
| // SME_QOS_STATUS_SETUP_REQ_PENDING_RSP |
| new_state = SME_QOS_REQUESTED; |
| pentry->reason = SME_QOS_REASON_SETUP; |
| //Need this info when addts comes back from PE to know on |
| //which index of the AC the request was from |
| pACInfo->tspec_pending = tmask; |
| } |
| pACInfo->num_flows[tmask - 1]++; |
| //indicate on which index the flow entry belongs to & add it to the |
| //Flow List at the end |
| pentry->tspec_mask = tmask; |
| pentry->QoSInfo = Tspec_Info; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d creating entry at %pK with flowID %d", |
| __func__, __LINE__, |
| sessionId, pentry, QosFlowID); |
| csrLLInsertTail(&sme_QosCb.flow_list, &pentry->link, VOS_TRUE); |
| } |
| else |
| { |
| // unexpected status returned by sme_QosSetup() |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d unexpected status %d " |
| "returned by sme_QosSetup", |
| __func__, __LINE__, |
| sessionId, status); |
| new_state = pACInfo->curr_state; |
| } |
| break; |
| case SME_QOS_CLOSED: |
| case SME_QOS_INIT: |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: setup requested in unexpected state = %d", |
| __func__, __LINE__, |
| pACInfo->curr_state); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| VOS_ASSERT(0); |
| new_state = pACInfo->curr_state; |
| } |
| |
| /* |
| * if current state is same as previous no need for transition, |
| * if we are doing reassoc & we are already in handoff state, no need to |
| * move to requested state. But make sure to set the previous state as |
| * requested state |
| */ |
| if((new_state != pACInfo->curr_state)&& |
| (!(pACInfo->reassoc_pending && |
| (SME_QOS_HANDOFF == pACInfo->curr_state)))) |
| { |
| sme_QosStateTransition(sessionId, ac, new_state); |
| } |
| |
| if(pACInfo->reassoc_pending && |
| (SME_QOS_HANDOFF == pACInfo->curr_state)) |
| { |
| pACInfo->prev_state = SME_QOS_REQUESTED; |
| } |
| if((SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) || |
| (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status)) |
| { |
| (void)sme_QosProcessBufferedCmd(sessionId); |
| } |
| return status; |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosInternalModifyReq() - The SME QoS internal function to request |
| for modification of certain QoS params on a flow running on a particular AC. |
| If the request involves admission control on the requested AC, HDD needs to |
| provide the necessary Traffic Specification (TSPEC) parameters & SME might |
| start the renegotiation process through ADDTS. |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pQoSInfo - Pointer to sme_QosWmmTspecInfo which contains the WMM TSPEC |
| related info as defined above, provided by HDD |
| \param QosFlowID - Identification per flow running on each AC generated by |
| SME. |
| It is only meaningful if the QoS setup for the flow has |
| been successful already |
| |
| \return SME_QOS_STATUS_SETUP_SUCCESS_RSP - Modification is successful. |
| |
| Other status means request failed |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| sme_QosStatusType sme_QosInternalModifyReq(tpAniSirGlobal pMac, |
| sme_QosWmmTspecInfo * pQoSInfo, |
| v_U32_t QosFlowID, |
| v_BOOL_t buffered_cmd) |
| { |
| tListElem *pEntry= NULL; |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosFlowInfoEntry *pNewEntry= NULL; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| sme_QosEdcaAcType ac; |
| sme_QosStates new_state = SME_QOS_CLOSED; |
| sme_QosStatusType status = SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| sme_QosWmmTspecInfo Aggr_Tspec_Info; |
| sme_QosSearchInfo search_key; |
| sme_QosCmdInfo cmd; |
| v_U8_t sessionId; |
| v_BOOL_t bufferCommand; |
| eHalStatus hstatus; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked for flow %d", |
| __func__, __LINE__, |
| QosFlowID); |
| |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| //set the key type & the key to be searched in the Flow List |
| search_key.key.QosFlowID = QosFlowID; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_1; |
| search_key.sessionId = SME_QOS_SEARCH_SESSION_ID_ANY; |
| //go through the link list to find out the details on the flow |
| pEntry = sme_QosFindInFlowList(search_key); |
| if(!pEntry) |
| { |
| //Err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: no match found for flowID = %d", |
| __func__, __LINE__, |
| QosFlowID); |
| return SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP; |
| } |
| // find the AC |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| ac = flow_info->ac_type; |
| |
| sessionId = flow_info->sessionId; |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| |
| //validate QoS params |
| if(!sme_QosValidateRequestedParams(pMac, pQoSInfo, sessionId)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: invalid params", |
| __func__, __LINE__); |
| return SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP; |
| } |
| // For modify, make sure that direction, TID and UP are not being altered |
| if((pQoSInfo->ts_info.direction != flow_info->QoSInfo.ts_info.direction) || |
| (pQoSInfo->ts_info.up != flow_info->QoSInfo.ts_info.up) || |
| (pQoSInfo->ts_info.tid != flow_info->QoSInfo.ts_info.tid)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Modification of direction/tid/up is not allowed", |
| __func__, __LINE__); |
| |
| return SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP; |
| } |
| |
| //should not be same as previous ioctl parameters |
| if ((pQoSInfo->nominal_msdu_size == flow_info->QoSInfo.nominal_msdu_size) && |
| (pQoSInfo->maximum_msdu_size == flow_info->QoSInfo.maximum_msdu_size) && |
| (pQoSInfo->min_data_rate == flow_info->QoSInfo.min_data_rate) && |
| (pQoSInfo->mean_data_rate == flow_info->QoSInfo.mean_data_rate) && |
| (pQoSInfo->peak_data_rate == flow_info->QoSInfo.peak_data_rate) && |
| (pQoSInfo->min_service_interval == |
| flow_info->QoSInfo.min_service_interval) && |
| (pQoSInfo->max_service_interval == |
| flow_info->QoSInfo.max_service_interval) && |
| (pQoSInfo->inactivity_interval == |
| flow_info->QoSInfo.inactivity_interval) && |
| (pQoSInfo->suspension_interval == |
| flow_info->QoSInfo.suspension_interval) && |
| (pQoSInfo->surplus_bw_allowance == |
| flow_info->QoSInfo.surplus_bw_allowance)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: the addts parameters are same as last request," |
| "dropping the current request", |
| __func__, __LINE__); |
| |
| return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| } |
| |
| /* Need to vote off power save for the duration of this request */ |
| pSession->readyForPowerSave = VOS_FALSE; |
| // assume we won't have to (re)buffer the command |
| bufferCommand = VOS_FALSE; |
| /* |
| * Check to consider the following flowing scenario Addts request is pending |
| * on one AC, while APSD requested on another which needs a reassoc. Will |
| * buffer a request if Addts is pending on any AC, which will safeguard the |
| * above scenario, & also won't confuse PE with back to back Addts or |
| * Addts followed by Reassoc. |
| */ |
| if(sme_QosIsRspPending(sessionId, ac)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: buffering the modify request for flow %d in state %d " |
| "since another request is pending", |
| __func__, __LINE__, |
| QosFlowID, pACInfo->curr_state ); |
| bufferCommand = VOS_TRUE; |
| } |
| else |
| { |
| // make sure we are in full power so that we can issue |
| // an AddTS or reassoc if necessary |
| if(!pMac->psOffloadEnabled) |
| { |
| hstatus = pmcRequestFullPower(pMac, sme_QosPmcFullPowerCallback, |
| pSession, eSME_REASON_OTHER); |
| } |
| else |
| { |
| hstatus = pmcOffloadRequestFullPower(pMac, sessionId, |
| sme_QosPmcOffloadFullPowerCallback, |
| pSession, eSME_REASON_OTHER); |
| } |
| |
| if( eHAL_STATUS_PMC_PENDING == hstatus ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: buffering the modify request for flow %d in state %d, " |
| "waiting for full power", |
| __func__, __LINE__, |
| QosFlowID, pACInfo->curr_state ); |
| bufferCommand = VOS_TRUE; |
| } |
| } |
| if (bufferCommand) |
| { |
| // we need to buffer the command |
| cmd.command = SME_QOS_MODIFY_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.modifyCmdInfo.QosFlowID = QosFlowID; |
| cmd.u.modifyCmdInfo.QoSInfo = *pQoSInfo; |
| hstatus = sme_QosBufferCmd(&cmd, buffered_cmd); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't buffer the modify request in state = %d", |
| __func__, __LINE__, |
| pACInfo->curr_state ); |
| /* Unable to buffer the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Buffered modify request for flow = %d", |
| __func__, __LINE__, |
| QosFlowID); |
| return SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; |
| } |
| //get into the stat m/c to see if the request can be granted |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_QOS_ON: |
| //save the new params adding a new (duplicate) entry in the Flow List |
| //Once we have decided on OTA exchange needed or not we can delete the |
| //original one from the List |
| pNewEntry = (sme_QosFlowInfoEntry *) vos_mem_malloc(sizeof(*pNewEntry)); |
| if (!pNewEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't allocate memory for the new " |
| "entry in the Flow List", |
| __func__, __LINE__); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| } |
| pNewEntry->ac_type = ac; |
| pNewEntry->sessionId = sessionId; |
| pNewEntry->HDDcontext = flow_info->HDDcontext; |
| pNewEntry->QoSCallback = flow_info->QoSCallback; |
| pNewEntry->QosFlowID = flow_info->QosFlowID; |
| pNewEntry->reason = SME_QOS_REASON_MODIFY_PENDING; |
| //since it is a modify request, use the same index on which the flow |
| //entry originally was running & add it to the Flow List at the end |
| pNewEntry->tspec_mask = flow_info->tspec_mask; |
| pNewEntry->QoSInfo = *pQoSInfo; |
| //update the entry from Flow List which needed to be modified |
| flow_info->reason = SME_QOS_REASON_MODIFY; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d creating modified " |
| "entry at %pK with flowID %d", |
| __func__, __LINE__, |
| sessionId, pNewEntry, pNewEntry->QosFlowID); |
| //add the new entry under construction to the Flow List |
| csrLLInsertTail(&sme_QosCb.flow_list, &pNewEntry->link, VOS_TRUE); |
| //update TSPEC with the new param set |
| hstatus = sme_QosUpdateParams(sessionId, |
| ac, pNewEntry->tspec_mask, |
| &Aggr_Tspec_Info); |
| if(HAL_STATUS_SUCCESS(hstatus)) |
| { |
| pACInfo->requested_QoSInfo[pNewEntry->tspec_mask -1] = Aggr_Tspec_Info; |
| //if ACM, send out a new ADDTS |
| status = sme_QosSetup(pMac, sessionId, |
| &pACInfo->requested_QoSInfo[pNewEntry->tspec_mask -1], |
| ac); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d with AC %d in state SME_QOS_QOS_ON " |
| "sme_QosSetup returned with status %d", |
| __func__, __LINE__, |
| sessionId, ac, status); |
| if(SME_QOS_STATUS_SETUP_REQ_PENDING_RSP != status) |
| { |
| /* We aren't waiting for a response from the AP |
| so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| } |
| if(SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status) |
| { |
| new_state = SME_QOS_REQUESTED; |
| status = SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; |
| pACInfo->tspec_pending = pNewEntry->tspec_mask; |
| } |
| else if((SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) || |
| (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status)) |
| { |
| new_state = SME_QOS_QOS_ON; |
| |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| //delete the original entry in FLOW list which got modified |
| search_key.key.ac_type = ac; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; |
| search_key.sessionId = sessionId; |
| hstatus = sme_QosFindAllInFlowList(pMac, search_key, |
| sme_QosModifyFnp); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| status = SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| } |
| if(SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP != status) |
| { |
| pACInfo->curr_QoSInfo[pNewEntry->tspec_mask -1] = |
| pACInfo->requested_QoSInfo[pNewEntry->tspec_mask -1]; |
| if(SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status) |
| { |
| status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY; |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| search_key.key.ac_type = ac; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; |
| search_key.sessionId = sessionId; |
| hstatus = sme_QosFindAllInFlowList(pMac, search_key, |
| sme_QosModificationNotifyFnp); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't notify other " |
| "entries on this AC =%d", |
| __func__, __LINE__, ac); |
| } |
| } |
| else if(SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) |
| { |
| status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP; |
| } |
| } |
| if(buffered_cmd) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pACInfo->curr_QoSInfo[pNewEntry->tspec_mask -1], |
| status, |
| flow_info->QosFlowID); |
| } |
| |
| } |
| else |
| { |
| // unexpected status returned by sme_QosSetup() |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d unexpected status %d " |
| "returned by sme_QosSetup", |
| __func__, __LINE__, |
| sessionId, status); |
| new_state = SME_QOS_QOS_ON; |
| } |
| } |
| else |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: sme_QosUpdateParams() failed", |
| __func__, __LINE__); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| new_state = SME_QOS_LINK_UP; |
| } |
| /* if we are doing reassoc & we are already in handoff state, no need |
| to move to requested state. But make sure to set the previous state |
| as requested state |
| */ |
| if(!(pACInfo->reassoc_pending && |
| (SME_QOS_HANDOFF == pACInfo->curr_state))) |
| { |
| sme_QosStateTransition(sessionId, ac, new_state); |
| } |
| else |
| { |
| pACInfo->prev_state = SME_QOS_REQUESTED; |
| } |
| break; |
| case SME_QOS_HANDOFF: |
| case SME_QOS_REQUESTED: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: Buffering modify request for flow %d in state = %d", |
| __func__, __LINE__, |
| QosFlowID, pACInfo->curr_state ); |
| //buffer cmd |
| cmd.command = SME_QOS_MODIFY_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.modifyCmdInfo.QosFlowID = QosFlowID; |
| cmd.u.modifyCmdInfo.QoSInfo = *pQoSInfo; |
| hstatus = sme_QosBufferCmd(&cmd, buffered_cmd); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't buffer the modify request in state = %d", |
| __func__, __LINE__, |
| pACInfo->curr_state ); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| } |
| status = SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; |
| break; |
| case SME_QOS_CLOSED: |
| case SME_QOS_INIT: |
| case SME_QOS_LINK_UP: |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: modify requested in unexpected state = %d", |
| __func__, __LINE__, |
| pACInfo->curr_state); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| break; |
| } |
| if((SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) || |
| (SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY == status)) |
| { |
| (void)sme_QosProcessBufferedCmd(sessionId); |
| } |
| return status; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosInternalReleaseReq() - The SME QoS internal function to request |
| for releasing a QoS flow running on a particular AC. |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param sessionId - sessionId returned by sme_OpenSession. |
| \param QosFlowID - Identification per flow running on each AC generated by SME |
| It is only meaningful if the QoS setup for the flow is |
| successful |
| |
| \return eHAL_STATUS_SUCCESS - Release is successful. |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| sme_QosStatusType sme_QosInternalReleaseReq(tpAniSirGlobal pMac, |
| uint8_t sessionId, |
| v_U32_t QosFlowID, |
| v_BOOL_t buffered_cmd) |
| { |
| tListElem *pEntry= NULL; |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| sme_QosFlowInfoEntry *pDeletedFlow = NULL; |
| sme_QosEdcaAcType ac; |
| sme_QosStates new_state = SME_QOS_CLOSED; |
| sme_QosStatusType status = SME_QOS_STATUS_RELEASE_FAILURE_RSP; |
| sme_QosWmmTspecInfo Aggr_Tspec_Info; |
| sme_QosSearchInfo search_key; |
| sme_QosCmdInfo cmd; |
| tCsrRoamModifyProfileFields modifyProfileFields; |
| v_BOOL_t deltsIssued = VOS_FALSE; |
| v_BOOL_t bufferCommand; |
| eHalStatus hstatus; |
| v_BOOL_t biDirectionalFlowsPresent = VOS_FALSE; |
| v_BOOL_t uplinkFlowsPresent = VOS_FALSE; |
| v_BOOL_t downlinkFlowsPresent = VOS_FALSE; |
| tListElem *pResult= NULL; |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked for flow %d", |
| __func__, __LINE__, |
| QosFlowID); |
| |
| if (!CSR_IS_SESSION_VALID(pMac, sessionId)) { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Session Id:%d is invalid", |
| __func__, __LINE__, sessionId); |
| return status; |
| } |
| |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| //set the key type & the key to be searched in the Flow List |
| search_key.key.QosFlowID = QosFlowID; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_1; |
| search_key.sessionId = SME_QOS_SEARCH_SESSION_ID_ANY; |
| //go through the link list to find out the details on the flow |
| pEntry = sme_QosFindInFlowList(search_key); |
| |
| if(!pEntry) |
| { |
| //Err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: no match found for flowID = %d", |
| __func__, __LINE__, |
| QosFlowID); |
| |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| if (!buffered_cmd && |
| !csrLLIsListEmpty(&pSession->bufferedCommandList, VOS_FALSE)) { |
| cmd.command = SME_QOS_RELEASE_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.releaseCmdInfo.QosFlowID = QosFlowID; |
| hstatus = sme_QosBufferCmd(&cmd, buffered_cmd); |
| if(HAL_STATUS_SUCCESS(hstatus)) { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Buffered release request for flow = %d", |
| __func__, __LINE__, QosFlowID); |
| } |
| } |
| return SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP; |
| } |
| // find the AC |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| ac = flow_info->ac_type; |
| sessionId = flow_info->sessionId; |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| /* Need to vote off power save for the duration of this request */ |
| pSession->readyForPowerSave = VOS_FALSE; |
| // assume we won't have to (re)buffer the command |
| bufferCommand = VOS_FALSE; |
| /* |
| * Check to consider the following flowing scenario Addts request is pending |
| * on one AC, while APSD requested on another which needs a reassoc. |
| * Will buffer a request if Addts is pending on any AC, which will safeguard |
| * the above scenario, & also won't confuse PE with back to back Addts or |
| * Addts followed by Reassoc. |
| */ |
| if(sme_QosIsRspPending(sessionId, ac)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: buffering the release request for flow %d in state %d " |
| "since another request is pending", |
| __func__, __LINE__, |
| QosFlowID, pACInfo->curr_state ); |
| bufferCommand = VOS_TRUE; |
| } |
| else |
| { |
| // make sure we are in full power so that we can issue |
| // a DelTS or reassoc if necessary |
| if(!pMac->psOffloadEnabled) |
| { |
| hstatus = pmcRequestFullPower(pMac, sme_QosPmcFullPowerCallback, |
| pSession, eSME_REASON_OTHER); |
| } |
| else |
| { |
| hstatus = pmcOffloadRequestFullPower(pMac, sessionId, |
| sme_QosPmcOffloadFullPowerCallback, |
| pSession, eSME_REASON_OTHER); |
| } |
| |
| if( eHAL_STATUS_PMC_PENDING == hstatus ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: buffering the release request for flow %d in state %d, " |
| "waiting for full power", |
| __func__, __LINE__, |
| QosFlowID, pACInfo->curr_state ); |
| bufferCommand = VOS_TRUE; |
| } |
| } |
| if (bufferCommand) |
| { |
| // we need to buffer the command |
| cmd.command = SME_QOS_RELEASE_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.releaseCmdInfo.QosFlowID = QosFlowID; |
| hstatus = sme_QosBufferCmd(&cmd, buffered_cmd); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't buffer the release request in state = %d", |
| __func__, __LINE__, |
| pACInfo->curr_state ); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return SME_QOS_STATUS_RELEASE_FAILURE_RSP; |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Buffered release request for flow = %d", |
| __func__, __LINE__, |
| QosFlowID); |
| return SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP; |
| } |
| //get into the stat m/c to see if the request can be granted |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_QOS_ON: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: tspec_mask_status = %d for AC = %d with " |
| "entry tspec_mask = %d", |
| __func__, __LINE__, |
| pACInfo->tspec_mask_status, ac, flow_info->tspec_mask); |
| |
| //check if multiple flows running on the ac |
| if(pACInfo->num_flows[flow_info->tspec_mask - 1] > 1) |
| { |
| //don't want to include the flow in the new TSPEC on which release |
| //is requested |
| flow_info->reason = SME_QOS_REASON_RELEASE; |
| |
| /* |
| * Check if the flow being released is for bi-directional. |
| * Following flows may present in the system. |
| * a) bi-directional flows |
| * b) uplink flows |
| * c) downlink flows. |
| * If the flow being released is for bidirectional, splitting of |
| * existing streams into two tspec indices is required in case ff (b), |
| * (c) are present and not (a). |
| * In case if split occurs, all upstreams are aggregated into tspec |
| * index 0, downstreams are aggregated into tspec index 1 and two |
| * tspec requests for (aggregated) upstream(s) followed by |
| * (aggregated) downstream(s) is sent to AP. |
| */ |
| if(flow_info->QoSInfo.ts_info.direction == SME_QOS_WMM_TS_DIR_BOTH) |
| { |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| //set the key type & the key to be searched in the Flow List |
| search_key.key.ac_type = ac; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_4; |
| search_key.sessionId = sessionId; |
| search_key.direction = SME_QOS_WMM_TS_DIR_BOTH; |
| pResult = sme_QosFindInFlowList(search_key); |
| if(pResult) |
| biDirectionalFlowsPresent = VOS_TRUE; |
| |
| if(!biDirectionalFlowsPresent) |
| { |
| // The only existing bidirectional flow is being released |
| |
| // Check if uplink flows exist |
| search_key.direction = SME_QOS_WMM_TS_DIR_UPLINK; |
| pResult = sme_QosFindInFlowList(search_key); |
| if(pResult) |
| uplinkFlowsPresent = VOS_TRUE; |
| |
| // Check if downlink flows exist |
| search_key.direction = SME_QOS_WMM_TS_DIR_DOWNLINK; |
| pResult = sme_QosFindInFlowList(search_key); |
| if(pResult) |
| downlinkFlowsPresent = VOS_TRUE; |
| |
| if(uplinkFlowsPresent && downlinkFlowsPresent) |
| { |
| // Need to split the uni-directional flows into SME_QOS_TSPEC_INDEX_0 and SME_QOS_TSPEC_INDEX_1 |
| |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| // Mark all downstream flows as using tspec index 1 |
| search_key.key.ac_type = ac; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_4; |
| search_key.sessionId = sessionId; |
| search_key.direction = SME_QOS_WMM_TS_DIR_DOWNLINK; |
| sme_QosUpdateTspecMask(sessionId, search_key, SME_QOS_TSPEC_MASK_BIT_2_SET); |
| |
| // Aggregate all downstream flows |
| hstatus = sme_QosUpdateParams(sessionId, |
| ac, SME_QOS_TSPEC_MASK_BIT_2_SET, |
| &Aggr_Tspec_Info); |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d buffering the AddTS request " |
| "for AC %d in state %d as Addts is pending " |
| "on other Tspec index of this AC", |
| __func__, __LINE__, |
| sessionId, ac, pACInfo->curr_state); |
| |
| /* |
| * Buffer the (aggregated) tspec request for downstream flows. |
| * Please note that the (aggregated) tspec for upstream flows is |
| * sent out by the subsequent logic. |
| */ |
| cmd.command = SME_QOS_RESEND_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.resendCmdInfo.ac = ac; |
| cmd.u.resendCmdInfo.tspecMask = SME_QOS_TSPEC_MASK_BIT_2_SET; |
| cmd.u.resendCmdInfo.QoSInfo = Aggr_Tspec_Info; |
| pACInfo->requested_QoSInfo[SME_QOS_TSPEC_MASK_BIT_2_SET - 1] = Aggr_Tspec_Info; |
| if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_FALSE))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d unable to buffer the AddTS " |
| "request for AC %d TSPEC %d in state %d", |
| __func__, __LINE__, |
| sessionId, ac, SME_QOS_TSPEC_MASK_BIT_2_SET, pACInfo->curr_state); |
| |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| |
| return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| } |
| pACInfo->tspec_mask_status = SME_QOS_TSPEC_MASK_BIT_1_2_SET; |
| |
| } |
| } |
| } |
| |
| /* In case of splitting of existing streams, |
| * tspec_mask will be pointing to tspec index 0 and |
| * aggregated tspec for upstream(s) is sent out here. */ |
| hstatus = sme_QosUpdateParams(sessionId, |
| ac, flow_info->tspec_mask, |
| &Aggr_Tspec_Info); |
| if(HAL_STATUS_SUCCESS(hstatus)) |
| { |
| pACInfo->requested_QoSInfo[flow_info->tspec_mask - 1] = Aggr_Tspec_Info; |
| //if ACM, send out a new ADDTS |
| status = sme_QosSetup(pMac, sessionId, |
| &pACInfo->requested_QoSInfo[flow_info->tspec_mask - 1], ac); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d with AC %d in state SME_QOS_QOS_ON " |
| "sme_QosSetup returned with status %d", |
| __func__, __LINE__, |
| sessionId, ac, status); |
| if(SME_QOS_STATUS_SETUP_REQ_PENDING_RSP != status) |
| { |
| /* We aren't waiting for a response from the AP |
| so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| } |
| if(SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status) |
| { |
| new_state = SME_QOS_REQUESTED; |
| status = SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP; |
| pACInfo->tspec_pending = flow_info->tspec_mask; |
| } |
| else if((SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) || |
| (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status)) |
| { |
| new_state = SME_QOS_QOS_ON; |
| pACInfo->num_flows[flow_info->tspec_mask - 1]--; |
| pACInfo->curr_QoSInfo[flow_info->tspec_mask - 1] = |
| pACInfo->requested_QoSInfo[flow_info->tspec_mask - 1]; |
| //delete the entry from Flow List |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Deleting entry at %pK with flowID %d", |
| __func__, __LINE__, |
| flow_info, QosFlowID); |
| csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE ); |
| pDeletedFlow = flow_info; |
| if(SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status) |
| { |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| search_key.key.ac_type = ac; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; |
| search_key.sessionId = sessionId; |
| hstatus = sme_QosFindAllInFlowList(pMac, search_key, |
| sme_QosSetupFnp); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't notify other " |
| "entries on this AC =%d", |
| __func__, __LINE__, ac); |
| } |
| } |
| status = SME_QOS_STATUS_RELEASE_SUCCESS_RSP; |
| if(buffered_cmd) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pACInfo->curr_QoSInfo[flow_info->tspec_mask - 1], |
| status, |
| flow_info->QosFlowID); |
| } |
| } |
| else |
| { |
| // unexpected status returned by sme_QosSetup() |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d unexpected status %d " |
| "returned by sme_QosSetup", |
| __func__, __LINE__, |
| sessionId, status); |
| new_state = SME_QOS_LINK_UP; |
| pACInfo->num_flows[flow_info->tspec_mask - 1]--; |
| pACInfo->curr_QoSInfo[flow_info->tspec_mask - 1] = |
| pACInfo->requested_QoSInfo[flow_info->tspec_mask - 1]; |
| //delete the entry from Flow List |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d deleting entry at " |
| "%pK with flowID %d", |
| __func__, __LINE__, |
| sessionId, flow_info, QosFlowID); |
| csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE ); |
| pDeletedFlow = flow_info; |
| if(buffered_cmd) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pACInfo->curr_QoSInfo[flow_info->tspec_mask - 1], |
| status, |
| flow_info->QosFlowID); |
| } |
| } |
| } |
| else |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: sme_QosUpdateParams() failed", |
| __func__, __LINE__); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| new_state = SME_QOS_LINK_UP; |
| if(buffered_cmd) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pACInfo->curr_QoSInfo[flow_info->tspec_mask - 1], |
| status, |
| flow_info->QosFlowID); |
| } |
| } |
| } |
| else |
| { |
| // this is the only flow aggregated in this TSPEC |
| status = SME_QOS_STATUS_RELEASE_SUCCESS_RSP; |
| #if defined(FEATURE_WLAN_ESE) && !defined(FEATURE_WLAN_ESE_UPLOAD) |
| if (ac == SME_QOS_EDCA_AC_VO) |
| { |
| // Indicate to neighbor roam logic of the new required VO |
| // ac bandwidth requirement. |
| csrNeighborRoamIndicateVoiceBW( pMac, pACInfo->curr_QoSInfo[0].peak_data_rate, FALSE ); |
| } |
| #endif |
| //check if delts needs to be sent |
| if(CSR_IS_ADDTS_WHEN_ACMOFF_SUPPORTED(pMac) || |
| sme_QosIsACM(pMac, pSession->assocInfo.pBssDesc, ac, NULL)) |
| { |
| //check if other TSPEC for this AC is also in use |
| if(SME_QOS_TSPEC_MASK_BIT_1_2_SET != pACInfo->tspec_mask_status) |
| { |
| // this is the only TSPEC active on this AC |
| // so indicate that we no longer require APSD |
| pSession->apsdMask &= ~(1 << (SME_QOS_EDCA_AC_VO - ac)); |
| //Also update modifyProfileFields.uapsd_mask in CSR for consistency |
| csrGetModifyProfileFields(pMac, flow_info->sessionId, &modifyProfileFields); |
| modifyProfileFields.uapsd_mask = pSession->apsdMask; |
| csrSetModifyProfileFields(pMac, flow_info->sessionId, &modifyProfileFields); |
| if(!pSession->apsdMask) |
| { |
| // this session no longer needs UAPSD |
| // do any sessions still require UAPSD? |
| if (!sme_QosIsUapsdActive()) |
| { |
| // No sessions require UAPSD so turn it off |
| // (really don't care when PMC stops it) |
| if(!pMac->psOffloadEnabled) |
| { |
| (void)pmcStopUapsd(pMac); |
| } |
| else |
| { |
| (void)pmcOffloadStopUapsd(pMac, sessionId); |
| } |
| } |
| } |
| } |
| if (SME_QOS_RELEASE_DEFAULT == pACInfo->relTrig) |
| { |
| //send delts |
| hstatus = qosIssueCommand(pMac, sessionId, eSmeCommandDelTs, |
| NULL, ac, flow_info->tspec_mask); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: sme_QosDelTsReq() failed", |
| __func__, __LINE__); |
| status = SME_QOS_STATUS_RELEASE_FAILURE_RSP; |
| /* We aren't waiting for a response from the AP |
| so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| } |
| else |
| { |
| pACInfo->tspec_mask_status &= SME_QOS_TSPEC_MASK_BIT_1_2_SET & |
| (~flow_info->tspec_mask); |
| deltsIssued = VOS_TRUE; |
| } |
| } |
| else |
| { |
| pSession->readyForPowerSave = VOS_TRUE; |
| pACInfo->tspec_mask_status &= SME_QOS_TSPEC_MASK_BIT_1_2_SET & |
| (~flow_info->tspec_mask); |
| deltsIssued = VOS_TRUE; |
| } |
| } |
| else if(pSession->apsdMask & (1 << (SME_QOS_EDCA_AC_VO - ac))) |
| { |
| //reassoc logic |
| csrGetModifyProfileFields(pMac, sessionId, &modifyProfileFields); |
| modifyProfileFields.uapsd_mask |= pSession->apsdMask; |
| modifyProfileFields.uapsd_mask &= ~(1 << (SME_QOS_EDCA_AC_VO - ac)); |
| pSession->apsdMask &= ~(1 << (SME_QOS_EDCA_AC_VO - ac)); |
| if(!pSession->apsdMask) |
| { |
| // this session no longer needs UAPSD |
| // do any sessions still require UAPSD? |
| if (!sme_QosIsUapsdActive()) |
| { |
| // No sessions require UAPSD so turn it off |
| // (really don't care when PMC stops it) |
| if(!pMac->psOffloadEnabled) |
| { |
| (void)pmcStopUapsd(pMac); |
| } |
| else |
| { |
| (void)pmcOffloadStopUapsd(pMac, sessionId); |
| } |
| } |
| } |
| hstatus = sme_QosRequestReassoc(pMac, sessionId, |
| &modifyProfileFields, VOS_FALSE); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Reassoc failed", |
| __func__, __LINE__); |
| status = SME_QOS_STATUS_RELEASE_FAILURE_RSP; |
| /* We aren't waiting for a response from the AP |
| so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| } |
| else |
| { |
| pACInfo->reassoc_pending = VOS_FALSE;//no need to wait |
| pACInfo->prev_state = SME_QOS_LINK_UP; |
| pACInfo->tspec_pending = 0; |
| } |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: nothing to do for AC = %d", |
| __func__, __LINE__, ac); |
| /* We aren't waiting for a response from the AP |
| so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| } |
| |
| if (SME_QOS_RELEASE_BY_AP == pACInfo->relTrig) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pACInfo->curr_QoSInfo[flow_info->tspec_mask - 1], |
| SME_QOS_STATUS_RELEASE_QOS_LOST_IND, |
| flow_info->QosFlowID); |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Deleting entry at %pK with flowID %d", |
| __func__, __LINE__, |
| flow_info, flow_info->QosFlowID); |
| } |
| else if(buffered_cmd) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| NULL, |
| status, |
| flow_info->QosFlowID); |
| } |
| |
| if(SME_QOS_STATUS_RELEASE_FAILURE_RSP == status) |
| { |
| break; |
| } |
| |
| if(((SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~flow_info->tspec_mask) > 0) && |
| ((SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~flow_info->tspec_mask) <= |
| SME_QOS_TSPEC_INDEX_MAX)) |
| { |
| if(pACInfo->num_flows[(SME_QOS_TSPEC_MASK_BIT_1_2_SET & |
| ~flow_info->tspec_mask) - 1] > 0) |
| { |
| new_state = SME_QOS_QOS_ON; |
| } |
| else |
| { |
| new_state = SME_QOS_LINK_UP; |
| } |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Exceeded the array bounds of pACInfo->num_flows", |
| __func__, __LINE__); |
| VOS_ASSERT (0); |
| return SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP; |
| } |
| |
| if(VOS_FALSE == deltsIssued) |
| { |
| vos_mem_zero(&pACInfo->curr_QoSInfo[flow_info->tspec_mask - 1], |
| sizeof(sme_QosWmmTspecInfo)); |
| } |
| vos_mem_zero(&pACInfo->requested_QoSInfo[flow_info->tspec_mask - 1], |
| sizeof(sme_QosWmmTspecInfo)); |
| pACInfo->num_flows[flow_info->tspec_mask - 1]--; |
| //delete the entry from Flow List |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d deleting entry at %pK with flowID %d", |
| __func__, __LINE__, |
| sessionId, flow_info, QosFlowID); |
| csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE ); |
| pDeletedFlow = flow_info; |
| pACInfo->relTrig = SME_QOS_RELEASE_DEFAULT; |
| } |
| /* if we are doing reassoc & we are already in handoff state, no need |
| to move to requested state. But make sure to set the previous state |
| as requested state |
| */ |
| if(SME_QOS_HANDOFF != pACInfo->curr_state) |
| { |
| sme_QosStateTransition(sessionId, ac, new_state); |
| } |
| if(pACInfo->reassoc_pending) |
| { |
| pACInfo->prev_state = SME_QOS_REQUESTED; |
| } |
| break; |
| case SME_QOS_HANDOFF: |
| case SME_QOS_REQUESTED: |
| //buffer cmd |
| cmd.command = SME_QOS_RELEASE_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.releaseCmdInfo.QosFlowID = QosFlowID; |
| hstatus = sme_QosBufferCmd(&cmd, buffered_cmd); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't buffer the release request in state = %d", |
| __func__, __LINE__, |
| pACInfo->curr_state ); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return SME_QOS_STATUS_RELEASE_FAILURE_RSP; |
| } |
| status = SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP; |
| break; |
| case SME_QOS_CLOSED: |
| case SME_QOS_INIT: |
| case SME_QOS_LINK_UP: |
| default: |
| //print error msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: release request in unexpected state = %d", |
| __func__, __LINE__, |
| pACInfo->curr_state ); |
| VOS_ASSERT(0); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| break; |
| } |
| // if we deleted a flow, reclaim the memory |
| if (pDeletedFlow) |
| { |
| vos_mem_free(pDeletedFlow); |
| } |
| if((SME_QOS_STATUS_RELEASE_SUCCESS_RSP == status)) |
| { |
| (void)sme_QosProcessBufferedCmd(sessionId); |
| } |
| return status; |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosSetup() - The internal qos setup function which has the |
| intelligence if the request is NOP, or for APSD and/or need to send out ADDTS. |
| It also does the sanity check for QAP, AP supports APSD etc. |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param sessionId - Session upon which setup is being performed |
| \param pTspec_Info - Pointer to sme_QosWmmTspecInfo which contains the WMM |
| TSPEC related info as defined above |
| \param ac - Enumeration of the various EDCA Access Categories. |
| |
| \return SME_QOS_STATUS_SETUP_SUCCESS_RSP if the setup is successful |
| The logic used in the code might be confusing. Trying to cover all the cases |
| here. |
| AP supports App wants ACM = 1 Already set APSD Result |
| | 0 | 0 | 0 | 0 | NO ACM NO APSD |
| | 0 | 0 | 0 | 1 | NO ACM NO APSD/INVALID |
| | 0 | 0 | 1 | 0 | ADDTS |
| | 0 | 0 | 1 | 1 | ADDTS |
| | 0 | 1 | 0 | 0 | FAILURE |
| | 0 | 1 | 0 | 1 | INVALID |
| | 0 | 1 | 1 | 0 | ADDTS |
| | 0 | 1 | 1 | 1 | ADDTS |
| | 1 | 0 | 0 | 0 | NO ACM NO APSD |
| | 1 | 0 | 0 | 1 | NO ACM NO APSD |
| | 1 | 0 | 1 | 0 | ADDTS |
| | 1 | 0 | 1 | 1 | ADDTS |
| | 1 | 1 | 0 | 0 | REASSOC |
| | 1 | 1 | 0 | 1 | NOP: APSD SET ALREADY |
| | 1 | 1 | 1 | 0 | ADDTS |
| | 1 | 1 | 1 | 1 | ADDTS |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| sme_QosStatusType sme_QosSetup(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| sme_QosWmmTspecInfo *pTspec_Info, |
| sme_QosEdcaAcType ac) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosStatusType status = SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| tDot11fBeaconIEs *pIes = NULL; |
| tCsrRoamModifyProfileFields modifyProfileFields; |
| eHalStatus hstatus; |
| if( !CSR_IS_SESSION_VALID( pMac, sessionId ) ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Session Id %d is invalid", |
| __func__, __LINE__, |
| sessionId); |
| return status; |
| } |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| if( !pSession->sessionActive ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Session %d is inactive", |
| __func__, __LINE__, |
| sessionId); |
| return status; |
| } |
| if(!pSession->assocInfo.pBssDesc) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Session %d has an Invalid BSS Descriptor", |
| __func__, __LINE__, |
| sessionId); |
| return status; |
| } |
| hstatus = csrGetParsedBssDescriptionIEs(pMac, |
| pSession->assocInfo.pBssDesc, |
| &pIes); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d unable to parse BSS IEs", |
| __func__, __LINE__, |
| sessionId); |
| return status; |
| } |
| |
| /* success so pIes was allocated */ |
| |
| if( !CSR_IS_QOS_BSS(pIes) ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d AP doesn't support QoS", |
| __func__, __LINE__, |
| sessionId); |
| vos_mem_free(pIes); |
| //notify HDD through the synchronous status msg |
| return SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP; |
| } |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_DEBUG, |
| "%s: %d: UAPSD/PSB set %d: ", __func__, __LINE__, |
| pTspec_Info->ts_info.psb); |
| |
| pACInfo = &pSession->ac_info[ac]; |
| do |
| { |
| // is ACM enabled for this AC? |
| if(CSR_IS_ADDTS_WHEN_ACMOFF_SUPPORTED(pMac) || |
| sme_QosIsACM(pMac, pSession->assocInfo.pBssDesc, ac, NULL)) |
| { |
| // ACM is enabled for this AC so we must send an AddTS |
| if(pTspec_Info->ts_info.psb && |
| !(pMac->psOffloadEnabled && pMac->pmcOffloadInfo.pmc[sessionId].UapsdEnabled) |
| && (!pMac->pmc.uapsdEnabled )) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Request is looking for APSD but PMC doesn't " |
| "have support for APSD", |
| __func__, __LINE__); |
| break; |
| } |
| |
| if (pTspec_Info->ts_info.psb && |
| !(pIes->WMMParams.qosInfo & SME_QOS_AP_SUPPORTS_APSD) && |
| !(pIes->WMMInfoAp.uapsd)) |
| { |
| // application is looking for APSD but AP doesn't support it |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d AP doesn't support APSD", |
| __func__, __LINE__, |
| sessionId); |
| break; |
| } |
| |
| if(SME_QOS_MAX_TID == pTspec_Info->ts_info.tid) |
| { |
| //App didn't set TID, generate one |
| pTspec_Info->ts_info.tid = |
| (v_U8_t)(SME_QOS_WMM_UP_NC - pTspec_Info->ts_info.up); |
| } |
| //addts logic |
| hstatus = qosIssueCommand(pMac, sessionId, eSmeCommandAddTs, |
| pTspec_Info, ac, 0); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: sme_QosAddTsReq() failed", |
| __func__, __LINE__); |
| break; |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d AddTS on AC %d is pending", |
| __func__, __LINE__, |
| sessionId, ac); |
| status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP; |
| break; |
| } |
| // ACM is not enabled for this AC |
| // Is the application looking for APSD? |
| if(0 == pTspec_Info->ts_info.psb) |
| { |
| //no, we don't need APSD |
| //but check the case, if the setup is called as a result of a release |
| // or modify which boils down to the fact that APSD was set on this AC |
| // but no longer needed - so we need a reassoc for the above case to |
| // let the AP know |
| if(pSession->apsdMask & (1 << (SME_QOS_EDCA_AC_VO - ac))) |
| { |
| // APSD was formerly enabled on this AC but is no longer required |
| // so we must reassociate |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d reassoc needed " |
| "to disable APSD on AC %d", |
| __func__, __LINE__, |
| sessionId, ac); |
| csrGetModifyProfileFields(pMac, sessionId, &modifyProfileFields); |
| modifyProfileFields.uapsd_mask |= pSession->apsdMask; |
| modifyProfileFields.uapsd_mask &= ~(1 << (SME_QOS_EDCA_AC_VO - ac)); |
| hstatus = sme_QosRequestReassoc(pMac, sessionId, |
| &modifyProfileFields, VOS_FALSE); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Unable to request reassociation", |
| __func__, __LINE__); |
| break; |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d reassociation to enable " |
| "APSD on AC %d is pending", |
| __func__, __LINE__, |
| sessionId, ac); |
| status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP; |
| pACInfo->reassoc_pending = VOS_TRUE; |
| } |
| } |
| else |
| { |
| // we don't need APSD on this AC |
| // and we don't currently have APSD on this AC |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Request is not looking for APSD & Admission " |
| "Control isn't mandatory for the AC", |
| __func__, __LINE__); |
| //return success right away |
| status = SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP; |
| } |
| break; |
| } |
| else if(!(pIes->WMMParams.qosInfo & SME_QOS_AP_SUPPORTS_APSD) && |
| !(pIes->WMMInfoAp.uapsd)) |
| { |
| // application is looking for APSD but AP doesn't support it |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d AP doesn't support APSD", |
| __func__, __LINE__, |
| sessionId); |
| break; |
| } |
| else if(pSession->apsdMask & (1 << (SME_QOS_EDCA_AC_VO - ac))) |
| { |
| // application is looking for APSD |
| // and it is already enabled on this AC |
| status = SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Request is looking for APSD and it is already " |
| "set for the AC", |
| __func__, __LINE__); |
| break; |
| } |
| else |
| { |
| // application is looking for APSD |
| // but it is not enabled on this AC |
| // so we need to reassociate |
| if(pMac->pmc.uapsdEnabled || (pMac->psOffloadEnabled |
| && pMac->pmcOffloadInfo.pmc[sessionId].UapsdEnabled)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d reassoc needed " |
| "to enable APSD on AC %d", |
| __func__, __LINE__, |
| sessionId, ac); |
| //reassoc logic |
| // update the UAPSD mask to include the new |
| // AC on which APSD is requested |
| csrGetModifyProfileFields(pMac, sessionId, &modifyProfileFields); |
| modifyProfileFields.uapsd_mask |= pSession->apsdMask; |
| modifyProfileFields.uapsd_mask |= 1 << (SME_QOS_EDCA_AC_VO - ac); |
| hstatus = sme_QosRequestReassoc(pMac, sessionId, |
| &modifyProfileFields, VOS_FALSE); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Unable to request reassociation", |
| __func__, __LINE__); |
| break; |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d reassociation to enable " |
| "APSD on AC %d is pending", |
| __func__, __LINE__, |
| sessionId, ac); |
| status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP; |
| pACInfo->reassoc_pending = VOS_TRUE; |
| } |
| } |
| else |
| { |
| //err msg: no support for APSD from PMC |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: no support for APSD or BMPS from PMC", |
| __func__, __LINE__); |
| } |
| } |
| }while(0); |
| |
| vos_mem_free(pIes); |
| return status; |
| } |
| |
| #if defined(FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR) |
| /* This is a dummy function now. But the purpose of me adding this was to |
| * delay the TSPEC processing till SET_KEY completes. This function can be |
| * used to do any SME_QOS processing after the SET_KEY. As of now, it is |
| * not required as we are ok with tspec getting programmed before set_key |
| * as the roam timings are measured without tspec in reassoc! |
| */ |
| eHalStatus sme_QosProcessSetKeySuccessInd(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN, |
| "########### Set Key Complete #############"); |
| (void)sme_QosProcessBufferedCmd(sessionId); |
| return eHAL_STATUS_SUCCESS; |
| } |
| #endif |
| |
| #ifdef FEATURE_WLAN_ESE |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosESESaveTspecResponse() - This function saves the TSPEC |
| parameters that came along in the TSPEC IE in the reassoc response |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param sessionId - SME session ID |
| \param pTspec - Pointer to the TSPEC IE from the reassoc rsp |
| \param ac - Access Category for which this TSPEC rsp is received |
| \param tspecIndex - flow/direction |
| |
| \return eHAL_STATUS_SUCCESS - Release is successful. |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosESESaveTspecResponse(tpAniSirGlobal pMac, v_U8_t sessionId, tDot11fIEWMMTSPEC *pTspec, v_U8_t ac, v_U8_t tspecIndex) |
| { |
| tpSirAddtsRsp pAddtsRsp = &sme_QosCb.sessionInfo[sessionId].ac_info[ac].addTsRsp[tspecIndex]; |
| |
| ac = sme_QosUPtoACMap[pTspec->user_priority]; |
| |
| vos_mem_zero(pAddtsRsp, sizeof(tSirAddtsRsp)); |
| |
| pAddtsRsp->messageType = eWNI_SME_ADDTS_RSP; |
| pAddtsRsp->length = sizeof(tSirAddtsRsp); |
| pAddtsRsp->rc = eSIR_SUCCESS; |
| pAddtsRsp->sessionId = sessionId; |
| pAddtsRsp->rsp.dialogToken = 0; |
| pAddtsRsp->rsp.status = eSIR_SUCCESS; |
| pAddtsRsp->rsp.wmeTspecPresent = pTspec->present; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| "%s: Copy Tspec to local data structure ac=%d, tspecIdx=%d", |
| __func__, ac, tspecIndex); |
| |
| if (pAddtsRsp->rsp.wmeTspecPresent) |
| { |
| //Copy TSPEC params received in assoc response to addts response |
| ConvertWMMTSPEC(pMac, &pAddtsRsp->rsp.tspec, pTspec); |
| } |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosESEProcessReassocTspecRsp() - This function processes the |
| WMM TSPEC IE in the reassoc response. Reassoc triggered as part of |
| ESE roaming to another ESE capable AP. If the TSPEC was added before |
| reassoc, as part of Call Admission Control, the reassoc req from the |
| STA would carry the TSPEC parameters which were already negotiated |
| with the older AP. |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param sessionId - SME session ID |
| \param pEven_info - Pointer to the smeJoinRsp structure |
| |
| \return eHAL_STATUS_SUCCESS - Release is successful. |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosESEProcessReassocTspecRsp(tpAniSirGlobal pMac, v_U8_t sessionId, void* pEvent_info) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| tDot11fIEWMMTSPEC *pTspecIE = NULL; |
| tCsrRoamSession *pCsrSession = NULL; |
| tCsrRoamConnectedInfo *pCsrConnectedInfo = NULL; |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| v_U8_t ac, numTspec, cnt; |
| v_U8_t tspec_flow_index, tspec_mask_status; |
| v_U32_t tspecIeLen; |
| |
| pCsrSession = CSR_GET_SESSION(pMac, sessionId); |
| if (NULL == pCsrSession) { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("session %d not found"), sessionId); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| pCsrConnectedInfo = &pCsrSession->connectedInfo; |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| |
| // Get the TSPEC IEs which came along with the reassoc response |
| // from the pbFrames pointer |
| pTspecIE = (tDot11fIEWMMTSPEC *)(pCsrConnectedInfo->pbFrames + pCsrConnectedInfo->nBeaconLength + |
| pCsrConnectedInfo->nAssocReqLength + pCsrConnectedInfo->nAssocRspLength + pCsrConnectedInfo->nRICRspLength); |
| |
| /* Get the number of tspecs Ies in the frame, the min length |
| should be at-least equal to the one TSPEC IE */ |
| tspecIeLen = pCsrConnectedInfo->nTspecIeLength; |
| if (tspecIeLen < sizeof(tDot11fIEWMMTSPEC)) { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("ESE Tspec IE len %d less than min %zu"), |
| tspecIeLen, sizeof(tDot11fIEWMMTSPEC)); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN, |
| "TspecLen = %d, pbFrames = %pK, pTspecIE = %pK", |
| tspecIeLen, pCsrConnectedInfo->pbFrames, pTspecIE); |
| |
| numTspec = (tspecIeLen)/sizeof(tDot11fIEWMMTSPEC); |
| for(cnt=0; cnt<numTspec; cnt++) { |
| ac = sme_QosUpToAc(pTspecIE->user_priority); |
| if (ac >= SME_QOS_EDCA_AC_MAX) { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("ac %d more than it`s max value"), ac); |
| return eHAL_STATUS_FAILURE; |
| } |
| pACInfo = &pSession->ac_info[ac]; |
| tspec_mask_status = pACInfo->tspec_mask_status; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN, |
| FL("UP=%d, ac=%d, tspec_mask_status=%x"), |
| pTspecIE->user_priority, ac, tspec_mask_status ); |
| |
| for (tspec_flow_index = 0; tspec_flow_index < SME_QOS_TSPEC_INDEX_MAX; tspec_flow_index++) { |
| if (tspec_mask_status & (1 << tspec_flow_index)) { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN, |
| FL("Found Tspec entry flow = %d AC = %d"),tspec_flow_index, ac); |
| sme_QosESESaveTspecResponse(pMac, sessionId, pTspecIE, ac, tspec_flow_index); |
| } else { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN, |
| FL("Not found Tspec entry flow = %d AC = %d"),tspec_flow_index, ac); |
| } |
| } |
| // Increment the pointer to point it to the next TSPEC IE |
| pTspecIE++; |
| } |
| |
| /* Send the Aggregated QoS request to HAL */ |
| status = sme_QosFTAggrQosReq(pMac,sessionId); |
| |
| return status; |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosCopyTspecInfo() - This function copies the existing TSPEC |
| parameters from the source structure to the destination structure. |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pTspec_Info - source structure |
| \param pTspec - destination structure |
| |
| \return void |
| --------------------------------------------------------------------------*/ |
| static void sme_QosCopyTspecInfo(tpAniSirGlobal pMac, sme_QosWmmTspecInfo *pTspec_Info, tSirMacTspecIE* pTspec) |
| { |
| /* As per WMM_AC_testplan_v0.39 Minimum Service Interval, Maximum Service |
| * Interval, Service Start Time, Suspension Interval and Delay Bound are |
| * all intended for HCCA operation and therefore must be set to zero*/ |
| pTspec->delayBound = pTspec_Info->delay_bound; |
| pTspec->inactInterval = pTspec_Info->inactivity_interval; |
| pTspec->length = SME_QOS_TSPEC_IE_LENGTH; |
| pTspec->maxBurstSz = pTspec_Info->max_burst_size; |
| pTspec->maxMsduSz = pTspec_Info->maximum_msdu_size; |
| pTspec->maxSvcInterval = pTspec_Info->max_service_interval; |
| pTspec->meanDataRate = pTspec_Info->mean_data_rate; |
| pTspec->mediumTime = pTspec_Info->medium_time; |
| pTspec->minDataRate = pTspec_Info->min_data_rate; |
| pTspec->minPhyRate = pTspec_Info->min_phy_rate; |
| pTspec->minSvcInterval = pTspec_Info->min_service_interval; |
| pTspec->nomMsduSz = pTspec_Info->nominal_msdu_size; |
| pTspec->peakDataRate = pTspec_Info->peak_data_rate; |
| pTspec->surplusBw = pTspec_Info->surplus_bw_allowance; |
| pTspec->suspendInterval = pTspec_Info->suspension_interval; |
| pTspec->svcStartTime = pTspec_Info->svc_start_time; |
| pTspec->tsinfo.traffic.direction = pTspec_Info->ts_info.direction; |
| pTspec->tsinfo.traffic.psb = pTspec_Info->ts_info.psb; |
| pTspec->tsinfo.traffic.tsid = pTspec_Info->ts_info.tid; |
| pTspec->tsinfo.traffic.userPrio = pTspec_Info->ts_info.up; |
| pTspec->tsinfo.traffic.accessPolicy = SME_QOS_ACCESS_POLICY_EDCA; |
| pTspec->tsinfo.traffic.burstSizeDefn = pTspec_Info->ts_info.burst_size_defn; |
| pTspec->tsinfo.traffic.ackPolicy = pTspec_Info->ts_info.ack_policy; |
| pTspec->type = SME_QOS_TSPEC_IE_TYPE; |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: up = %d, tid = %d", |
| __func__, __LINE__, |
| pTspec_Info->ts_info.up, |
| pTspec_Info->ts_info.tid); |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosEseRetrieveTspecInfo() - This function is called by CSR |
| when try to create reassoc request message to PE - csrSendSmeReassocReqMsg |
| This functions get the existing tspec parameters to be included |
| in the reassoc request. |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param sessionId - SME session ID |
| \param pTspecInfo - Pointer to the structure to carry back the TSPEC parameters |
| |
| \return v_U8_t - number of existing negotiated TSPECs |
| --------------------------------------------------------------------------*/ |
| v_U8_t sme_QosEseRetrieveTspecInfo(tpAniSirGlobal pMac, v_U8_t sessionId, tTspecInfo *pTspecInfo) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| v_U8_t tspec_mask_status = 0; |
| v_U8_t tspec_pending_status = 0; |
| v_U8_t ac, numTspecs = 0; |
| tTspecInfo *pDstTspec = pTspecInfo; |
| |
| //TODO: Check if TSPEC has already been established, if not return |
| |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) { |
| volatile v_U8_t tspec_index = 0; |
| |
| pACInfo = &pSession->ac_info[ac]; |
| tspec_pending_status = pACInfo->tspec_pending; |
| tspec_mask_status = pACInfo->tspec_mask_status; |
| |
| do { |
| if (tspec_mask_status & SME_QOS_TSPEC_MASK_BIT_1_SET) { |
| /* If a tspec status is pending, take requested_QoSInfo for RIC request, else use curr_QoSInfo |
| for the RIC request */ |
| if (tspec_pending_status & SME_QOS_TSPEC_MASK_BIT_1_SET) { |
| sme_QosCopyTspecInfo(pMac, &pACInfo->requested_QoSInfo[tspec_index], &pDstTspec->tspec); |
| } else { |
| sme_QosCopyTspecInfo(pMac, &pACInfo->curr_QoSInfo[tspec_index], &pDstTspec->tspec); |
| } |
| pDstTspec->valid = TRUE; |
| numTspecs++; |
| pDstTspec++; |
| } |
| tspec_mask_status >>= 1; |
| tspec_pending_status >>= 1; |
| tspec_index++; |
| } while (tspec_mask_status); |
| } |
| |
| return numTspecs; |
| } |
| |
| #endif |
| |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| |
| eHalStatus sme_QosCreateTspecRICIE(tpAniSirGlobal pMac, sme_QosWmmTspecInfo *pTspec_Info, |
| v_U8_t *pRICBuffer, v_U32_t *pRICLength, v_U8_t *pRICIdentifier) |
| { |
| tDot11fIERICDataDesc ricIE; |
| tANI_U32 nStatus; |
| |
| if (pRICBuffer == NULL || pRICIdentifier == NULL || pRICLength == NULL) |
| { |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| vos_mem_zero(&ricIE, sizeof(tDot11fIERICDataDesc)); |
| |
| ricIE.present = 1; |
| ricIE.RICData.present = 1; |
| ricIE.RICData.resourceDescCount = 1; |
| ricIE.RICData.statusCode = 0; |
| ricIE.RICData.Identifier = sme_QosAssignDialogToken(); |
| #ifndef USE_80211_WMMTSPEC_FOR_RIC |
| ricIE.TSPEC.present = 1; |
| ricIE.TSPEC.delay_bound = pTspec_Info->delay_bound; |
| ricIE.TSPEC.inactivity_int = pTspec_Info->inactivity_interval; |
| ricIE.TSPEC.burst_size = pTspec_Info->max_burst_size; |
| ricIE.TSPEC.max_msdu_size = pTspec_Info->maximum_msdu_size; |
| ricIE.TSPEC.max_service_int = pTspec_Info->max_service_interval; |
| ricIE.TSPEC.mean_data_rate = pTspec_Info->mean_data_rate; |
| ricIE.TSPEC.medium_time = 0; |
| ricIE.TSPEC.min_data_rate = pTspec_Info->min_data_rate; |
| ricIE.TSPEC.min_phy_rate = pTspec_Info->min_phy_rate; |
| ricIE.TSPEC.min_service_int = pTspec_Info->min_service_interval; |
| ricIE.TSPEC.size = pTspec_Info->nominal_msdu_size; |
| ricIE.TSPEC.peak_data_rate = pTspec_Info->peak_data_rate; |
| ricIE.TSPEC.surplus_bw_allowance = pTspec_Info->surplus_bw_allowance; |
| ricIE.TSPEC.suspension_int = pTspec_Info->suspension_interval; |
| ricIE.TSPEC.service_start_time = pTspec_Info->svc_start_time; |
| ricIE.TSPEC.direction = pTspec_Info->ts_info.direction; |
| ricIE.TSPEC.psb = pTspec_Info->ts_info.psb; |
| ricIE.TSPEC.tsid = pTspec_Info->ts_info.tid; |
| ricIE.TSPEC.user_priority = pTspec_Info->ts_info.up; |
| ricIE.TSPEC.access_policy = SME_QOS_ACCESS_POLICY_EDCA; |
| |
| *pRICIdentifier = ricIE.RICData.Identifier; |
| |
| nStatus = dot11fPackIeRICDataDesc(pMac, &ricIE, pRICBuffer, sizeof(ricIE), pRICLength); |
| if (DOT11F_FAILED(nStatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("Packing of RIC Data of length %d failed with status %d"), |
| *pRICLength, nStatus); |
| } |
| #else // WMM TSPEC |
| /*As per WMM_AC_testplan_v0.39 Minimum Service Interval, Maximum Service |
| Interval, Service Start Time, Suspension Interval and Delay Bound are |
| all intended for HCCA operation and therefore must be set to zero*/ |
| ricIE.WMMTSPEC.present = 1; |
| ricIE.WMMTSPEC.version = 1; |
| ricIE.WMMTSPEC.delay_bound = pTspec_Info->delay_bound; |
| ricIE.WMMTSPEC.inactivity_int = pTspec_Info->inactivity_interval; |
| ricIE.WMMTSPEC.burst_size = pTspec_Info->max_burst_size; |
| ricIE.WMMTSPEC.max_msdu_size = pTspec_Info->maximum_msdu_size; |
| ricIE.WMMTSPEC.max_service_int = pTspec_Info->max_service_interval; |
| ricIE.WMMTSPEC.mean_data_rate = pTspec_Info->mean_data_rate; |
| ricIE.WMMTSPEC.medium_time = 0; |
| ricIE.WMMTSPEC.min_data_rate = pTspec_Info->min_data_rate; |
| ricIE.WMMTSPEC.min_phy_rate = pTspec_Info->min_phy_rate; |
| ricIE.WMMTSPEC.min_service_int = pTspec_Info->min_service_interval; |
| ricIE.WMMTSPEC.size = pTspec_Info->nominal_msdu_size; |
| ricIE.WMMTSPEC.peak_data_rate = pTspec_Info->peak_data_rate; |
| ricIE.WMMTSPEC.surplus_bw_allowance = pTspec_Info->surplus_bw_allowance; |
| ricIE.WMMTSPEC.suspension_int = pTspec_Info->suspension_interval; |
| ricIE.WMMTSPEC.service_start_time = pTspec_Info->svc_start_time; |
| ricIE.WMMTSPEC.direction = pTspec_Info->ts_info.direction; |
| ricIE.WMMTSPEC.psb = pTspec_Info->ts_info.psb; |
| ricIE.WMMTSPEC.tsid = pTspec_Info->ts_info.tid; |
| ricIE.WMMTSPEC.user_priority = pTspec_Info->ts_info.up; |
| ricIE.WMMTSPEC.access_policy = SME_QOS_ACCESS_POLICY_EDCA; |
| |
| |
| nStatus = dot11fPackIeRICDataDesc(pMac, &ricIE, pRICBuffer, sizeof(ricIE), pRICLength); |
| if (DOT11F_FAILED(nStatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("Packing of RIC Data of length %d failed with status %d"), |
| *pRICLength, nStatus); |
| } |
| #endif /* 80211_TSPEC */ |
| *pRICIdentifier = ricIE.RICData.Identifier; |
| return nStatus; |
| } |
| |
| eHalStatus sme_QosProcessFTReassocReqEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| v_U8_t ac, qos_requested = FALSE; |
| v_U8_t tspec_flow_index; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| tListElem *pEntry= NULL; |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| FL("Invoked on session %d"), sessionId); |
| |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| qos_requested = FALSE; |
| |
| for (tspec_flow_index = 0; tspec_flow_index < SME_QOS_TSPEC_INDEX_MAX; tspec_flow_index++) |
| { |
| /* Only in the below case, copy the AC's curr QoS Info to requested QoS info */ |
| if ((pACInfo->ricIdentifier[tspec_flow_index] && !pACInfo->tspec_pending) || |
| (pACInfo->tspec_mask_status & (1<<tspec_flow_index))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| FL("Copying the currentQos to requestedQos for AC=%d, flow=%d"), |
| ac, tspec_flow_index ); |
| |
| pACInfo->requested_QoSInfo[tspec_flow_index] = pACInfo->curr_QoSInfo[tspec_flow_index]; |
| vos_mem_zero(&pACInfo->curr_QoSInfo[tspec_flow_index], sizeof(sme_QosWmmTspecInfo)); |
| qos_requested = TRUE; |
| } |
| } |
| |
| // Only if the tspec is required, transition the state to |
| // SME_QOS_REQUESTED for this AC |
| if (qos_requested) |
| { |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_HANDOFF: |
| sme_QosStateTransition(sessionId, ac, SME_QOS_REQUESTED); |
| break; |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("FT Reassoc req event in unexpected state %d"), pACInfo->curr_state); |
| VOS_ASSERT(0); |
| } |
| } |
| |
| } |
| |
| /* At this point of time, we are disconnected from the old AP, so it is safe |
| * to reset all these session variables */ |
| pSession->apsdMask = 0; |
| pSession->uapsdAlreadyRequested = 0; |
| pSession->readyForPowerSave = 0; |
| |
| /* Now change reason and HO renewal of all the flow in this session only */ |
| pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE ); |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN, |
| "%s: %d: Flow List empty, nothing to update", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| do |
| { |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| if(sessionId == flow_info->sessionId) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Changing FlowID %d reason to SETUP and HO renewal to FALSE", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| flow_info->reason = SME_QOS_REASON_SETUP; |
| flow_info->hoRenewal = eANI_BOOLEAN_TRUE; |
| } |
| pEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE ); |
| } while( pEntry ); |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| |
| eHalStatus sme_QosFTAggrQosReq( tpAniSirGlobal pMac, v_U8_t sessionId ) |
| { |
| tSirAggrQosReq *pMsg = NULL; |
| sme_QosSessionInfo *pSession; |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| int i, j = 0; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d", __func__, __LINE__, |
| sessionId); |
| |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| |
| pMsg = (tSirAggrQosReq *)vos_mem_malloc(sizeof(tSirAggrQosReq)); |
| |
| if (!pMsg) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't allocate memory for the msg buffer", |
| __func__, __LINE__); |
| |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| vos_mem_zero(pMsg, sizeof(tSirAggrQosReq)); |
| |
| pMsg->messageType = pal_cpu_to_be16((v_U16_t)eWNI_SME_FT_AGGR_QOS_REQ); |
| pMsg->length = sizeof(tSirAggrQosReq); |
| pMsg->sessionId = sessionId; |
| pMsg->timeout = 0; |
| pMsg->rspReqd = VOS_TRUE; |
| vos_mem_copy( &pMsg->bssId[ 0 ], |
| &pSession->assocInfo.pBssDesc->bssId[ 0 ], |
| sizeof(tCsrBssid) ); |
| |
| for( i = 0; i < SME_QOS_EDCA_AC_MAX; i++ ) |
| { |
| for( j = 0; j < SME_QOS_TSPEC_INDEX_MAX; j++ ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| FL("ac=%d, tspec_mask_staus=%x, tspec_index=%d"), |
| i, pSession->ac_info[i].tspec_mask_status, j); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| FL("direction = %d"), pSession->ac_info[i].addTsRsp[j].rsp.tspec.tsinfo.traffic.direction); |
| // Check if any flow is active on this AC |
| if ((pSession->ac_info[i].tspec_mask_status) & (1 << j)) |
| { |
| tANI_U8 direction = pSession->ac_info[i].addTsRsp[j].rsp.tspec.tsinfo.traffic.direction; |
| if ((direction == SME_QOS_WMM_TS_DIR_UPLINK) || |
| (direction == SME_QOS_WMM_TS_DIR_BOTH)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN, |
| FL("Found tspec entry AC=%d, flow=%d, direction = %d"), i, j, direction); |
| pMsg->aggrInfo.aggrAddTsInfo[i].dialogToken = |
| sme_QosAssignDialogToken(); |
| pMsg->aggrInfo.aggrAddTsInfo[i].lleTspecPresent = |
| pSession->ac_info[i].addTsRsp[j].rsp.lleTspecPresent; |
| pMsg->aggrInfo.aggrAddTsInfo[i].numTclas = |
| pSession->ac_info[i].addTsRsp[j].rsp.numTclas; |
| vos_mem_copy( pMsg->aggrInfo.aggrAddTsInfo[i].tclasInfo, |
| pSession->ac_info[i].addTsRsp[j].rsp.tclasInfo, |
| SIR_MAC_TCLASIE_MAXNUM ); |
| pMsg->aggrInfo.aggrAddTsInfo[i].tclasProc = |
| pSession->ac_info[i].addTsRsp[j].rsp.tclasProc; |
| pMsg->aggrInfo.aggrAddTsInfo[i].tclasProcPresent = |
| pSession->ac_info[i].addTsRsp[j].rsp.tclasProcPresent; |
| pMsg->aggrInfo.aggrAddTsInfo[i].tspec = |
| pSession->ac_info[i].addTsRsp[j].rsp.tspec; |
| pMsg->aggrInfo.aggrAddTsInfo[i].wmeTspecPresent = |
| pSession->ac_info[i].addTsRsp[j].rsp.wmeTspecPresent; |
| pMsg->aggrInfo.aggrAddTsInfo[i].wsmTspecPresent = |
| pSession->ac_info[i].addTsRsp[j].rsp.wsmTspecPresent; |
| pMsg->aggrInfo.tspecIdx |= ( 1 << i ); |
| |
| // Mark the index for this AC as pending for response, which would be |
| // used to validate the AddTS response from HAL->PE->SME |
| pSession->ac_info[i].tspec_pending = (1<<j); |
| } |
| } |
| } |
| } |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| "Sending aggregated message to HAL 0x%x", pMsg->aggrInfo.tspecIdx); |
| |
| if(HAL_STATUS_SUCCESS(palSendMBMessage(pMac->hHdd, pMsg))) |
| { |
| status = eHAL_STATUS_SUCCESS; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: sent down a AGGR QoS req to PE", |
| __func__, __LINE__); |
| } |
| |
| return status; |
| } |
| |
| eHalStatus sme_QosProcessFTRICResponse(tpAniSirGlobal pMac, v_U8_t sessionId, tDot11fIERICDataDesc *pRicDataDesc, v_U8_t ac, v_U8_t tspecIndex) |
| { |
| tANI_U8 i = 0; |
| tpSirAddtsRsp pAddtsRsp |
| = &sme_QosCb.sessionInfo[sessionId].ac_info[ac].addTsRsp[tspecIndex]; |
| |
| vos_mem_zero(pAddtsRsp, sizeof(tSirAddtsRsp)); |
| |
| pAddtsRsp->messageType = eWNI_SME_ADDTS_RSP; |
| pAddtsRsp->length = sizeof(tSirAddtsRsp); |
| pAddtsRsp->rc = pRicDataDesc->RICData.statusCode; |
| pAddtsRsp->sessionId = sessionId; |
| pAddtsRsp->rsp.dialogToken = pRicDataDesc->RICData.Identifier; |
| pAddtsRsp->rsp.status = pRicDataDesc->RICData.statusCode; |
| pAddtsRsp->rsp.wmeTspecPresent = pRicDataDesc->TSPEC.present; |
| if (pAddtsRsp->rsp.wmeTspecPresent) |
| { |
| //Copy TSPEC params received in RIC response to addts response |
| ConvertTSPEC(pMac, &pAddtsRsp->rsp.tspec, &pRicDataDesc->TSPEC); |
| } |
| |
| pAddtsRsp->rsp.numTclas = pRicDataDesc->num_TCLAS; |
| if (pAddtsRsp->rsp.numTclas) |
| { |
| for (i = 0; i < pAddtsRsp->rsp.numTclas; i++) |
| { |
| //Copy TCLAS info per index to the addts response |
| ConvertTCLAS(pMac, &pAddtsRsp->rsp.tclasInfo[i], &pRicDataDesc->TCLAS[i]); |
| } |
| } |
| |
| pAddtsRsp->rsp.tclasProcPresent = pRicDataDesc->TCLASSPROC.present; |
| if (pAddtsRsp->rsp.tclasProcPresent) |
| pAddtsRsp->rsp.tclasProc = pRicDataDesc->TCLASSPROC.processing; |
| |
| |
| pAddtsRsp->rsp.schedulePresent = pRicDataDesc->Schedule.present; |
| if (pAddtsRsp->rsp.schedulePresent) |
| { |
| //Copy Schedule IE params to addts response |
| ConvertSchedule(pMac, &pAddtsRsp->rsp.schedule, &pRicDataDesc->Schedule); |
| } |
| |
| //Need to check the below portion is a part of WMM TSPEC |
| //Process Delay element |
| if (pRicDataDesc->TSDelay.present) |
| ConvertTSDelay(pMac, &pAddtsRsp->rsp.delay, &pRicDataDesc->TSDelay); |
| |
| //Need to call for WMMTSPEC |
| if (pRicDataDesc->WMMTSPEC.present) |
| { |
| ConvertWMMTSPEC(pMac, &pAddtsRsp->rsp.tspec, &pRicDataDesc->WMMTSPEC); |
| } |
| //return sme_QosProcessAddTsRsp(pMac, &addtsRsp); |
| return eHAL_STATUS_SUCCESS; |
| } |
| eHalStatus sme_QosProcessAggrQosRsp(tpAniSirGlobal pMac, void *pMsgBuf) |
| { |
| tpSirAggrQosRsp pAggrRsp = (tpSirAggrQosRsp)pMsgBuf; |
| tSirAddtsRsp addtsRsp; |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| int i, j = 0; |
| tANI_U8 sessionId = pAggrRsp->sessionId; |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| FL("Received AGGR_QOS resp from LIM")); |
| |
| /* Copy over the updated response information for TSPEC of all the ACs */ |
| for( i = 0; i < SIR_QOS_NUM_AC_MAX; i++ ) |
| { |
| tANI_U8 tspec_mask_status = sme_QosCb.sessionInfo[sessionId].ac_info[i].tspec_mask_status; |
| for( j = 0; j < SME_QOS_TSPEC_INDEX_MAX; j++ ) |
| { |
| tANI_U8 direction = sme_QosCb.sessionInfo[sessionId].ac_info[i]. |
| addTsRsp[j].rsp.tspec.tsinfo.traffic.direction; |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| FL("Addts rsp from LIM AC=%d, flow=%d dir=%d, tspecIdx=%x"), |
| i, j, direction, pAggrRsp->aggrInfo.tspecIdx); |
| // Check if the direction is Uplink or bi-directional |
| if( ((1<<i) & pAggrRsp->aggrInfo.tspecIdx) && |
| ((tspec_mask_status) & (1<<j)) && |
| ((direction == SME_QOS_WMM_TS_DIR_UPLINK) || |
| (direction == SME_QOS_WMM_TS_DIR_BOTH))) |
| { |
| addtsRsp = sme_QosCb.sessionInfo[sessionId].ac_info[i].addTsRsp[j]; |
| addtsRsp.rc = pAggrRsp->aggrInfo.aggrRsp[i].status; |
| addtsRsp.rsp.status = pAggrRsp->aggrInfo.aggrRsp[i].status; |
| addtsRsp.rsp.tspec = pAggrRsp->aggrInfo.aggrRsp[i].tspec; |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| FL("Processing Addts rsp from LIM AC=%d, flow=%d"), i, j); |
| /* post ADD TS response for each */ |
| if (sme_QosProcessAddTsRsp(pMac, &addtsRsp) != eHAL_STATUS_SUCCESS) |
| { |
| status = eHAL_STATUS_FAILURE; |
| } |
| } |
| } |
| } |
| return status; |
| } |
| |
| |
| eHalStatus sme_QosProcessFTReassocRspEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| v_U8_t ac; |
| v_U8_t tspec_flow_index; |
| tDot11fIERICDataDesc *pRicDataDesc = NULL; |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| tCsrRoamSession *pCsrSession = CSR_GET_SESSION( pMac, sessionId ); |
| tCsrRoamConnectedInfo *pCsrConnectedInfo = NULL; |
| tANI_U32 ricRspLen; |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| tDot11fIERICDataDesc *pRicData = NULL; |
| tANI_U32 ricLen; |
| v_BOOL_t Found = false; |
| sme_QosWmmDirType direction; |
| v_U8_t ac1; |
| #endif |
| |
| if (NULL == pCsrSession) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("The Session pointer is NULL")); |
| return eHAL_STATUS_FAILURE; |
| } |
| pCsrConnectedInfo = &pCsrSession->connectedInfo; |
| |
| ricRspLen = pCsrConnectedInfo->nRICRspLength; |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d", |
| __func__, __LINE__, |
| sessionId); |
| |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| |
| pRicDataDesc = (tDot11fIERICDataDesc *)((pCsrConnectedInfo->pbFrames) + |
| (pCsrConnectedInfo->nBeaconLength + pCsrConnectedInfo->nAssocReqLength + |
| pCsrConnectedInfo->nAssocRspLength)); |
| |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| if(!pCsrSession->roamOffloadSynchParams.bRoamSynchInProgress) |
| { |
| #endif |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| |
| for (tspec_flow_index = 0; tspec_flow_index < SME_QOS_TSPEC_INDEX_MAX; tspec_flow_index++) |
| { |
| /* Only in the below case, copy the AC's curr QoS Info to requested QoS info */ |
| if (pACInfo->ricIdentifier[tspec_flow_index]) |
| { |
| |
| if (!ricRspLen) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("RIC Response not received for AC %d on TSPEC Index %d, RIC Req Identifier = %d"), |
| ac, tspec_flow_index, pACInfo->ricIdentifier[tspec_flow_index]); |
| VOS_ASSERT(0); |
| } |
| else |
| { |
| /* Now we got response for this identifier. Process it. */ |
| if (pRicDataDesc->present) |
| { |
| if (pRicDataDesc->RICData.present) |
| { |
| if (pRicDataDesc->RICData.Identifier != pACInfo->ricIdentifier[tspec_flow_index]) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("RIC response order not same as request sent. Request ID = %d, Response ID = %d"), |
| pACInfo->ricIdentifier[tspec_flow_index], pRicDataDesc->RICData.Identifier); |
| VOS_ASSERT(0); |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| FL("Processing RIC Response for AC %d, TSPEC Flow index %d with RIC ID %d "), |
| ac, tspec_flow_index, pRicDataDesc->RICData.Identifier); |
| status = sme_QosProcessFTRICResponse(pMac, sessionId, pRicDataDesc, ac, tspec_flow_index); |
| if (eHAL_STATUS_SUCCESS != status) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("Failed with status %d for AC %d in TSPEC Flow index = %d"), |
| status, ac, tspec_flow_index); |
| } |
| } |
| pRicDataDesc++; |
| ricRspLen -= sizeof(tDot11fIERICDataDesc); |
| } |
| } |
| } |
| } |
| |
| } |
| } |
| |
| if (ricRspLen) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("RIC Response still follows despite traversing through all ACs. Remaining len = %d"), ricRspLen); |
| VOS_ASSERT(0); |
| } |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| } |
| else |
| { |
| /* It means LFR3.0 roaming with RIC, |
| * currently we have support for WMM TSPEC alone |
| * In LFR3.0 11r since we do not have a RIC identifier |
| * maintained in host so identify the tspec from the AC |
| * and direction info */ |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| FL("LFR3-11r Compare RIC in Reassoc Resp to find" |
| " matching tspec in host.")); |
| |
| pRicData = pRicDataDesc; |
| ricLen = ricRspLen; |
| |
| if (ricRspLen && pRicDataDesc->present && |
| pRicDataDesc->WMMTSPEC.present) { |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| for (tspec_flow_index = 0; |
| tspec_flow_index < SME_QOS_TSPEC_INDEX_MAX; |
| tspec_flow_index++) { |
| if((pSession->ac_info[ac].tspec_mask_status) |
| & (1 << tspec_flow_index)) { |
| do { |
| ac1 = sme_QosUpToAc(pRicData->WMMTSPEC.user_priority); |
| if (ac == SME_QOS_EDCA_AC_MAX) { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("Invalid AC %d UP %d"), ac, |
| pRicData->WMMTSPEC.user_priority); |
| break; |
| } |
| direction = pRicData->WMMTSPEC.direction; |
| |
| if (ac == ac1 && |
| direction == pACInfo->requested_QoSInfo[tspec_flow_index].ts_info.direction) |
| { |
| /* It means we found a matching tspec */ |
| Found = true; |
| status = sme_QosProcessFTRICResponse(pMac, |
| sessionId, |
| pRicData, |
| ac, |
| tspec_flow_index); |
| if (eHAL_STATUS_SUCCESS != status) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("Failed with status %d for AC %d in TSPEC Flow index = %d"), |
| status, ac, tspec_flow_index); |
| } |
| break; |
| } |
| pRicData++; |
| ricLen -= sizeof(tDot11fIERICDataDesc); |
| }while(ricLen); |
| } |
| pRicData = pRicDataDesc; |
| ricLen = ricRspLen; |
| Found = false; |
| } |
| } |
| }else { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| FL("LFR3-11r ricRspLen is zero or pRicDataDesc is not" |
| " present or wmmtspec is not present")); |
| } |
| } |
| #endif |
| |
| /* Send the Aggregated QoS request to HAL */ |
| status = sme_QosFTAggrQosReq(pMac,sessionId); |
| |
| return status; |
| } |
| |
| #endif /* WLAN_FEATURE_VOWIFI_11R */ |
| |
| |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosAddTsReq() - To send down the ADDTS request with TSPEC params |
| to PE |
| |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param sessionId - Session upon which the TSPEC should be added |
| \param pTspec_Info - Pointer to sme_QosWmmTspecInfo which contains the WMM |
| TSPEC related info as defined above |
| \param ac - Enumeration of the various EDCA Access Categories. |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosAddTsReq(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| sme_QosWmmTspecInfo * pTspec_Info, |
| sme_QosEdcaAcType ac) |
| { |
| tSirAddtsReq *pMsg = NULL; |
| sme_QosSessionInfo *pSession; |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| #ifdef FEATURE_WLAN_ESE |
| tCsrRoamSession *pCsrSession = CSR_GET_SESSION( pMac, sessionId ); |
| #endif |
| #ifdef FEATURE_WLAN_DIAG_SUPPORT |
| WLAN_VOS_DIAG_EVENT_DEF(qos, vos_event_wlan_qos_payload_type); |
| #endif |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d for AC %d", |
| __func__, __LINE__, |
| sessionId, ac); |
| if (sessionId >= CSR_ROAM_SESSION_MAX) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: sessionId(%d) is invalid", |
| __func__, __LINE__, sessionId); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| pMsg = (tSirAddtsReq *)vos_mem_malloc(sizeof(tSirAddtsReq)); |
| if (!pMsg) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't allocate memory for the msg buffer", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| vos_mem_zero(pMsg, sizeof(tSirAddtsReq)); |
| pMsg->messageType = pal_cpu_to_be16((v_U16_t)eWNI_SME_ADDTS_REQ); |
| pMsg->length = sizeof(tSirAddtsReq); |
| pMsg->sessionId = sessionId; |
| pMsg->timeout = 0; |
| pMsg->rspReqd = VOS_TRUE; |
| pMsg->req.dialogToken = sme_QosAssignDialogToken(); |
| /*As per WMM_AC_testplan_v0.39 Minimum Service Interval, Maximum Service |
| Interval, Service Start Time, Suspension Interval and Delay Bound are |
| all intended for HCCA operation and therefore must be set to zero*/ |
| pMsg->req.tspec.delayBound = 0; |
| pMsg->req.tspec.inactInterval = pTspec_Info->inactivity_interval; |
| pMsg->req.tspec.length = SME_QOS_TSPEC_IE_LENGTH; |
| pMsg->req.tspec.maxBurstSz = pTspec_Info->max_burst_size; |
| pMsg->req.tspec.maxMsduSz = pTspec_Info->maximum_msdu_size; |
| pMsg->req.tspec.maxSvcInterval = pTspec_Info->max_service_interval; |
| pMsg->req.tspec.meanDataRate = pTspec_Info->mean_data_rate; |
| pMsg->req.tspec.mediumTime = pTspec_Info->medium_time; |
| pMsg->req.tspec.minDataRate = pTspec_Info->min_data_rate; |
| pMsg->req.tspec.minPhyRate = pTspec_Info->min_phy_rate; |
| pMsg->req.tspec.minSvcInterval = pTspec_Info->min_service_interval; |
| pMsg->req.tspec.nomMsduSz = pTspec_Info->nominal_msdu_size; |
| pMsg->req.tspec.peakDataRate = pTspec_Info->peak_data_rate; |
| pMsg->req.tspec.surplusBw = pTspec_Info->surplus_bw_allowance; |
| pMsg->req.tspec.suspendInterval = pTspec_Info->suspension_interval; |
| pMsg->req.tspec.svcStartTime = 0; |
| pMsg->req.tspec.tsinfo.traffic.direction = pTspec_Info->ts_info.direction; |
| //Make sure UAPSD is allowed. BTC may want to disable UAPSD while keep QoS setup |
| if( pTspec_Info->ts_info.psb) |
| { |
| pMsg->req.tspec.tsinfo.traffic.psb = pTspec_Info->ts_info.psb; |
| } |
| else |
| { |
| pMsg->req.tspec.tsinfo.traffic.psb = 0; |
| pTspec_Info->ts_info.psb = 0; |
| } |
| pMsg->req.tspec.tsinfo.traffic.tsid = pTspec_Info->ts_info.tid; |
| pMsg->req.tspec.tsinfo.traffic.userPrio = pTspec_Info->ts_info.up; |
| pMsg->req.tspec.tsinfo.traffic.accessPolicy = SME_QOS_ACCESS_POLICY_EDCA; |
| pMsg->req.tspec.tsinfo.traffic.burstSizeDefn = pTspec_Info->ts_info.burst_size_defn; |
| pMsg->req.tspec.tsinfo.traffic.ackPolicy = pTspec_Info->ts_info.ack_policy; |
| pMsg->req.tspec.type = SME_QOS_TSPEC_IE_TYPE; |
| /*Fill the BSSID pMsg->req.bssId*/ |
| if (NULL == pSession->assocInfo.pBssDesc) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: BSS descriptor is NULL so we don't send request to PE", |
| __func__, __LINE__); |
| vos_mem_free(pMsg); |
| return eHAL_STATUS_FAILURE; |
| } |
| vos_mem_copy( &pMsg->bssId[ 0 ], |
| &pSession->assocInfo.pBssDesc->bssId[ 0 ], |
| sizeof(tCsrBssid) ); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: up = %d, tid = %d", |
| __func__, __LINE__, |
| pTspec_Info->ts_info.up, |
| pTspec_Info->ts_info.tid); |
| #ifdef FEATURE_WLAN_ESE |
| if(pCsrSession->connectedProfile.isESEAssoc) |
| { |
| pMsg->req.tsrsIE.tsid = pTspec_Info->ts_info.up; |
| pMsg->req.tsrsPresent = 1; |
| } |
| #endif |
| if(HAL_STATUS_SUCCESS(palSendMBMessage(pMac->hHdd, pMsg))) |
| { |
| status = eHAL_STATUS_SUCCESS; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: sent down a ADDTS req to PE", |
| __func__, __LINE__); |
| //event: EVENT_WLAN_QOS |
| #ifdef FEATURE_WLAN_DIAG_SUPPORT |
| qos.eventId = SME_QOS_DIAG_ADDTS_REQ; |
| qos.reasonCode = SME_QOS_DIAG_USER_REQUESTED; |
| WLAN_VOS_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS); |
| #endif //FEATURE_WLAN_DIAG_SUPPORT |
| } |
| return status; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosDelTsReq() - To send down the DELTS request with TSPEC params |
| to PE |
| |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param sessionId - Session from which the TSPEC should be deleted |
| \param ac - Enumeration of the various EDCA Access Categories. |
| \param tspec_mask - on which tspec per AC, the delts is requested |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosDelTsReq(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| sme_QosEdcaAcType ac, |
| v_U8_t tspec_mask) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| tSirDeltsReq *pMsg; |
| sme_QosWmmTspecInfo *pTspecInfo; |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| #ifdef FEATURE_WLAN_DIAG_SUPPORT |
| WLAN_VOS_DIAG_EVENT_DEF(qos, vos_event_wlan_qos_payload_type); |
| #endif |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d for AC %d", |
| __func__, __LINE__, |
| sessionId, ac); |
| pMsg = (tSirDeltsReq *)vos_mem_malloc(sizeof(tSirDeltsReq)); |
| if (!pMsg) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't allocate memory for the msg buffer", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| vos_mem_zero(pMsg, sizeof(tSirDeltsReq)); |
| // get pointer to the TSPEC being deleted |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| pTspecInfo = &pACInfo->curr_QoSInfo[tspec_mask - 1]; |
| pMsg->messageType = pal_cpu_to_be16((v_U16_t)eWNI_SME_DELTS_REQ); |
| pMsg->length = sizeof(tSirDeltsReq); |
| pMsg->sessionId = sessionId; |
| pMsg->rspReqd = VOS_TRUE; |
| pMsg->req.tspec.delayBound = pTspecInfo->delay_bound; |
| pMsg->req.tspec.inactInterval = pTspecInfo->inactivity_interval; |
| pMsg->req.tspec.length = SME_QOS_TSPEC_IE_LENGTH; |
| pMsg->req.tspec.maxBurstSz = pTspecInfo->max_burst_size; |
| pMsg->req.tspec.maxMsduSz = pTspecInfo->maximum_msdu_size; |
| pMsg->req.tspec.maxSvcInterval = pTspecInfo->max_service_interval; |
| pMsg->req.tspec.meanDataRate = pTspecInfo->mean_data_rate; |
| pMsg->req.tspec.mediumTime = pTspecInfo->medium_time; |
| pMsg->req.tspec.minDataRate = pTspecInfo->min_data_rate; |
| pMsg->req.tspec.minPhyRate = pTspecInfo->min_phy_rate; |
| pMsg->req.tspec.minSvcInterval = pTspecInfo->min_service_interval; |
| pMsg->req.tspec.nomMsduSz = pTspecInfo->nominal_msdu_size; |
| pMsg->req.tspec.peakDataRate = pTspecInfo->peak_data_rate; |
| pMsg->req.tspec.surplusBw = pTspecInfo->surplus_bw_allowance; |
| pMsg->req.tspec.suspendInterval = pTspecInfo->suspension_interval; |
| pMsg->req.tspec.svcStartTime = pTspecInfo->svc_start_time; |
| pMsg->req.tspec.tsinfo.traffic.direction = pTspecInfo->ts_info.direction; |
| pMsg->req.tspec.tsinfo.traffic.psb = pTspecInfo->ts_info.psb; |
| pMsg->req.tspec.tsinfo.traffic.tsid = pTspecInfo->ts_info.tid; |
| pMsg->req.tspec.tsinfo.traffic.userPrio = pTspecInfo->ts_info.up; |
| pMsg->req.tspec.tsinfo.traffic.accessPolicy = SME_QOS_ACCESS_POLICY_EDCA; |
| pMsg->req.tspec.tsinfo.traffic.burstSizeDefn = pTspecInfo->ts_info.burst_size_defn; |
| pMsg->req.tspec.tsinfo.traffic.ackPolicy = pTspecInfo->ts_info.ack_policy; |
| pMsg->req.tspec.type = SME_QOS_TSPEC_IE_TYPE; |
| /*Fill the BSSID pMsg->req.bssId*/ |
| if (NULL == pSession->assocInfo.pBssDesc) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: BSS descriptor is NULL so we don't send request to PE", |
| __func__, __LINE__); |
| vos_mem_free(pMsg); |
| return eHAL_STATUS_FAILURE; |
| } |
| vos_mem_copy( &pMsg->bssId[ 0 ], |
| &pSession->assocInfo.pBssDesc->bssId[ 0 ], |
| sizeof(tCsrBssid) ); |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: up = %d, tid = %d", |
| __func__, __LINE__, |
| pTspecInfo->ts_info.up, |
| pTspecInfo->ts_info.tid); |
| vos_mem_zero(&pACInfo->curr_QoSInfo[tspec_mask - 1], |
| sizeof(sme_QosWmmTspecInfo)); |
| if(HAL_STATUS_SUCCESS(palSendMBMessage(pMac->hHdd, pMsg))) |
| { |
| status = eHAL_STATUS_SUCCESS; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: sme_QosDelTsReq:Test: sent down a DELTS req to PE", |
| __func__, __LINE__); |
| //event: EVENT_WLAN_QOS |
| #ifdef FEATURE_WLAN_DIAG_SUPPORT |
| qos.eventId = SME_QOS_DIAG_DELTS; |
| qos.reasonCode = SME_QOS_DIAG_USER_REQUESTED; |
| WLAN_VOS_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS); |
| #endif //FEATURE_WLAN_DIAG_SUPPORT |
| } |
| |
| return status; |
| } |
| |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessAddTsRsp() - Function to process the |
| eWNI_SME_ADDTS_RSP came from PE |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pMsgBuf - Pointer to the msg buffer came from PE. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessAddTsRsp(tpAniSirGlobal pMac, void *pMsgBuf) |
| { |
| tpSirAddtsRsp paddts_rsp = (tpSirAddtsRsp)pMsgBuf; |
| sme_QosSessionInfo *pSession; |
| v_U8_t sessionId = paddts_rsp->sessionId; |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| sme_QosWmmUpType up = (sme_QosWmmUpType)paddts_rsp->rsp.tspec.tsinfo.traffic.userPrio; |
| sme_QosACInfo *pACInfo; |
| sme_QosEdcaAcType ac; |
| #endif |
| #ifdef FEATURE_WLAN_DIAG_SUPPORT |
| WLAN_VOS_DIAG_EVENT_DEF(qos, vos_event_wlan_qos_payload_type); |
| #endif |
| |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d for UP %d", |
| __func__, __LINE__, |
| sessionId, up); |
| |
| ac = sme_QosUpToAc(up); |
| if(SME_QOS_EDCA_AC_MAX == ac) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: invalid AC %d from UP %d", |
| __func__, __LINE__, ac, up); |
| |
| return eHAL_STATUS_FAILURE; |
| } |
| pACInfo = &pSession->ac_info[ac]; |
| if (SME_QOS_HANDOFF == pACInfo->curr_state) |
| { |
| smsLog(pMac, LOG1, FL("ADDTS Response received for AC %d in HANDOFF State.. Dropping"), ac); |
| pSession->readyForPowerSave = VOS_TRUE; |
| return eHAL_STATUS_SUCCESS; |
| } |
| #endif |
| |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Invoked on session %d with return code %d", |
| __func__, __LINE__, |
| sessionId, paddts_rsp->rc); |
| /* Our outstanding request has been serviced |
| we can go into power save */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| if(paddts_rsp->rc) |
| { |
| //event: EVENT_WLAN_QOS |
| #ifdef FEATURE_WLAN_DIAG_SUPPORT |
| qos.eventId = SME_QOS_DIAG_ADDTS_RSP; |
| qos.reasonCode = SME_QOS_DIAG_ADDTS_REFUSED; |
| WLAN_VOS_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS); |
| #endif //FEATURE_WLAN_DIAG_SUPPORT |
| status = sme_QosProcessAddTsFailureRsp(pMac, sessionId, &paddts_rsp->rsp); |
| } |
| else |
| { |
| status = sme_QosProcessAddTsSuccessRsp(pMac, sessionId, &paddts_rsp->rsp); |
| } |
| return status; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessDelTsRsp() - Function to process the |
| eWNI_SME_DELTS_RSP came from PE |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pMsgBuf - Pointer to the msg buffer came from PE. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessDelTsRsp(tpAniSirGlobal pMac, void *pMsgBuf) |
| { |
| tpSirDeltsRsp pDeltsRsp = (tpSirDeltsRsp)pMsgBuf; |
| sme_QosSessionInfo *pSession; |
| v_U8_t sessionId = pDeltsRsp->sessionId; |
| // msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Invoked on session %d with return code %d", |
| __func__, __LINE__, |
| sessionId, pDeltsRsp->rc); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| /* Our outstanding request has been serviced |
| we can go into power save */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| (void)sme_QosProcessBufferedCmd(sessionId); |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessDelTsInd() - Function to process the |
| eWNI_SME_DELTS_IND came from PE |
| |
| Since it's a DELTS indication from AP, will notify all the flows running on |
| this AC about QoS release |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pMsgBuf - Pointer to the msg buffer came from PE. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessDelTsInd(tpAniSirGlobal pMac, void *pMsgBuf) |
| { |
| tpSirDeltsRsp pdeltsind = (tpSirDeltsRsp)pMsgBuf; |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| v_U8_t sessionId = pdeltsind->sessionId; |
| sme_QosEdcaAcType ac; |
| sme_QosSearchInfo search_key; |
| sme_QosWmmUpType up = (sme_QosWmmUpType)pdeltsind->rsp.tspec.tsinfo.traffic.userPrio; |
| #ifdef FEATURE_WLAN_DIAG_SUPPORT |
| WLAN_VOS_DIAG_EVENT_DEF(qos, vos_event_wlan_qos_payload_type); |
| #endif |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Invoked on session %d for UP %d", |
| __func__, __LINE__, |
| sessionId, up); |
| ac = sme_QosUpToAc(up); |
| if(SME_QOS_EDCA_AC_MAX == ac) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: invalid AC %d from UP %d", |
| __func__, __LINE__, |
| ac, up); |
| return eHAL_STATUS_FAILURE; |
| } |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| //set the key type & the key to be searched in the Flow List |
| search_key.key.ac_type = ac; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; |
| search_key.sessionId = sessionId; |
| /* |
| * Find all Flows on the particular AC & delete them, also send HDD |
| * indication through the callback it registered per request |
| */ |
| if(!HAL_STATUS_SUCCESS(sme_QosFindAllInFlowList(pMac, search_key, sme_QosDelTsIndFnp))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: no match found for ac = %d", |
| __func__, __LINE__, |
| search_key.key.ac_type); |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| //event: EVENT_WLAN_QOS |
| #ifdef FEATURE_WLAN_DIAG_SUPPORT |
| qos.eventId = SME_QOS_DIAG_DELTS; |
| qos.reasonCode = SME_QOS_DIAG_DELTS_IND_FROM_AP; |
| WLAN_VOS_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS); |
| #endif //FEATURE_WLAN_DIAG_SUPPORT |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessAssocCompleteEv() - Function to process the |
| SME_QOS_CSR_ASSOC_COMPLETE event indication from CSR |
| \param pEvent_info - Pointer to relevant info from CSR. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessAssocCompleteEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| sme_QosEdcaAcType ac = SME_QOS_EDCA_AC_BE; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d", |
| __func__, __LINE__, |
| sessionId); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| if(((SME_QOS_INIT == pSession->ac_info[SME_QOS_EDCA_AC_BE].curr_state)&& |
| (SME_QOS_INIT == pSession->ac_info[SME_QOS_EDCA_AC_BK].curr_state)&& |
| (SME_QOS_INIT == pSession->ac_info[SME_QOS_EDCA_AC_VI].curr_state)&& |
| (SME_QOS_INIT == pSession->ac_info[SME_QOS_EDCA_AC_VO].curr_state)) || |
| (pSession->handoffRequested)) |
| { |
| //get the association info |
| if(!pEvent_info) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: pEvent_info is NULL", |
| __func__, __LINE__); |
| return status; |
| } |
| if(!((sme_QosAssocInfo *)pEvent_info)->pBssDesc) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: pBssDesc is NULL", |
| __func__, __LINE__); |
| return status; |
| } |
| if((pSession->assocInfo.pBssDesc) && |
| (csrIsBssidMatch(pMac, (tCsrBssid *)&pSession->assocInfo.pBssDesc->bssId, |
| (tCsrBssid *) &(((sme_QosAssocInfo *)pEvent_info)->pBssDesc->bssId)))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: assoc with the same BSS, no update needed", |
| __func__, __LINE__); |
| } |
| else |
| { |
| status = sme_QosSaveAssocInfo(pSession, pEvent_info); |
| } |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: wrong state: BE %d, BK %d, VI %d, VO %d", |
| __func__, __LINE__, |
| pSession->ac_info[SME_QOS_EDCA_AC_BE].curr_state, |
| pSession->ac_info[SME_QOS_EDCA_AC_BK].curr_state, |
| pSession->ac_info[SME_QOS_EDCA_AC_VI].curr_state, |
| pSession->ac_info[SME_QOS_EDCA_AC_VO].curr_state); |
| VOS_ASSERT(0); |
| return status; |
| } |
| // the session is active |
| pSession->sessionActive = VOS_TRUE; |
| if(pSession->handoffRequested) |
| { |
| pSession->handoffRequested = VOS_FALSE; |
| //renew all flows |
| (void)sme_QosProcessBufferedCmd(sessionId); |
| status = eHAL_STATUS_SUCCESS; |
| } |
| else |
| { |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_INIT: |
| sme_QosStateTransition(sessionId, ac, SME_QOS_LINK_UP); |
| break; |
| case SME_QOS_LINK_UP: |
| case SME_QOS_REQUESTED: |
| case SME_QOS_QOS_ON: |
| case SME_QOS_HANDOFF: |
| case SME_QOS_CLOSED: |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d AC %d is in wrong state %d", |
| __func__, __LINE__, |
| sessionId, ac, pACInfo->curr_state); |
| VOS_ASSERT(0); |
| break; |
| } |
| } |
| } |
| return status; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessReassocReqEv() - Function to process the |
| SME_QOS_CSR_REASSOC_REQ event indication from CSR |
| \param pEvent_info - Pointer to relevant info from CSR. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessReassocReqEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosEdcaAcType ac; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d", |
| __func__, __LINE__, |
| sessionId); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| if(pSession->ftHandoffInProgress) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: no need for state transition, should " |
| "already be in handoff state", |
| __func__, __LINE__); |
| if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[3].curr_state != SME_QOS_HANDOFF)) |
| { |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| sme_QosProcessFTReassocReqEv(pMac, sessionId, pEvent_info); |
| return eHAL_STATUS_SUCCESS; |
| } |
| #endif |
| |
| if(pSession->handoffRequested) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: no need for state transition, should " |
| "already be in handoff state", |
| __func__, __LINE__); |
| |
| if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[3].curr_state != SME_QOS_HANDOFF)) |
| { |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| //buffer the existing flows to be renewed after handoff is done |
| sme_QosBufferExistingFlows(pMac, sessionId); |
| //clean up the control block partially for handoff |
| sme_QosCleanupCtrlBlkForHandoff(pMac, sessionId); |
| return eHAL_STATUS_SUCCESS; |
| } |
| //TBH: Assuming both handoff algo & 11r willn't be enabled at the same time |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| if(pSession->ftHandoffInProgress) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: no need for state transition, should " |
| "already be in handoff state", |
| __func__, __LINE__); |
| |
| if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[3].curr_state != SME_QOS_HANDOFF)) |
| { |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| sme_QosProcessFTReassocReqEv(pMac, sessionId, pEvent_info); |
| return eHAL_STATUS_SUCCESS; |
| } |
| #endif |
| |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_LINK_UP: |
| case SME_QOS_REQUESTED: |
| case SME_QOS_QOS_ON: |
| sme_QosStateTransition(sessionId, ac, SME_QOS_HANDOFF); |
| break; |
| case SME_QOS_HANDOFF: |
| //This is normal because sme_QosRequestReassoc may already change the state |
| break; |
| case SME_QOS_CLOSED: |
| case SME_QOS_INIT: |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d AC %d is in wrong state %d", |
| __func__, __LINE__, |
| sessionId, ac, pACInfo->curr_state); |
| VOS_ASSERT(0); |
| break; |
| } |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessReassocSuccessEv() - Function to process the |
| SME_QOS_CSR_REASSOC_COMPLETE event indication from CSR |
| \param pEvent_info - Pointer to relevant info from CSR. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessReassocSuccessEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info) |
| { |
| |
| tCsrRoamSession *pCsrRoamSession = NULL; |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosEdcaAcType ac, ac_index; |
| sme_QosSearchInfo search_key; |
| sme_QosSearchInfo search_key1; |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| tListElem *pEntry= NULL; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d", |
| __func__, __LINE__, |
| sessionId); |
| |
| if (CSR_ROAM_SESSION_MAX <= sessionId) { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: invoked on session %d", |
| __func__, __LINE__, |
| sessionId); |
| return status; |
| } |
| |
| pCsrRoamSession = CSR_GET_SESSION( pMac, sessionId ); |
| |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| /* Our pending reassociation has completed |
| we can allow power save */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| //get the association info |
| if(!pEvent_info) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: pEvent_info is NULL", |
| __func__, __LINE__); |
| return status; |
| } |
| if(!((sme_QosAssocInfo *)pEvent_info)->pBssDesc) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: pBssDesc is NULL", |
| __func__, __LINE__); |
| return status; |
| } |
| status = sme_QosSaveAssocInfo(pSession, pEvent_info); |
| if(status) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: sme_QosSaveAssocInfo() failed", |
| __func__, __LINE__); |
| } |
| //TBH: Assuming both handoff algo & 11r willn't be enabled at the same time |
| if(pSession->handoffRequested) |
| { |
| pSession->handoffRequested = VOS_FALSE; |
| //renew all flows |
| (void)sme_QosProcessBufferedCmd(sessionId); |
| return eHAL_STATUS_SUCCESS; |
| } |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| if (pSession->ftHandoffInProgress) |
| { |
| if (csrRoamIs11rAssoc(pMac, sessionId)) { |
| if (pCsrRoamSession && |
| pCsrRoamSession->connectedInfo.nRICRspLength) { |
| status = sme_QosProcessFTReassocRspEv(pMac, sessionId, |
| pEvent_info); |
| } |
| } |
| #ifdef FEATURE_WLAN_ESE |
| // If ESE association check for TSPEC IEs in the reassoc rsp frame |
| if (csrRoamIsESEAssoc(pMac, sessionId)) { |
| if (pCsrRoamSession && |
| pCsrRoamSession->connectedInfo.nTspecIeLength) { |
| status = sme_QosESEProcessReassocTspecRsp(pMac, sessionId, |
| pEvent_info); |
| } |
| } |
| #endif |
| pSession->ftHandoffInProgress = VOS_FALSE; |
| pSession->handoffRequested = VOS_FALSE; |
| return status; |
| } |
| #endif |
| |
| pSession->sessionActive = VOS_TRUE; |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_HANDOFF: |
| // return to our previous state |
| sme_QosStateTransition(sessionId, ac, pACInfo->prev_state); |
| //for which ac APSD (hence the reassoc) is requested |
| if(pACInfo->reassoc_pending) |
| { |
| //update the apsd mask in CB - make sure to take care of the case |
| //where we are resetting the bit in apsd_mask |
| if(pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0].ts_info.psb) |
| { |
| pSession->apsdMask |= 1 << (SME_QOS_EDCA_AC_VO - ac); |
| } |
| else |
| { |
| pSession->apsdMask &= ~(1 << (SME_QOS_EDCA_AC_VO - ac)); |
| } |
| pACInfo->reassoc_pending = VOS_FALSE; |
| //during setup it gets set as addts & reassoc both gets a pending flag |
| //pACInfo->tspec_pending = 0; |
| sme_QosStateTransition(sessionId, ac, SME_QOS_QOS_ON); |
| // notify HDD with new Service Interval |
| pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0] = |
| pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0]; |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| //set the key type & the key to be searched in the Flow List |
| search_key.key.ac_type = ac; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; |
| search_key.sessionId = sessionId; |
| //notify PMC that reassoc is done for APSD on certain AC?? |
| |
| vos_mem_zero(&search_key1, sizeof(sme_QosSearchInfo)); |
| //set the hoRenewal field in control block if needed |
| search_key1.index = SME_QOS_SEARCH_KEY_INDEX_3; |
| search_key1.key.reason = SME_QOS_REASON_SETUP; |
| search_key1.sessionId = sessionId; |
| for(ac_index = SME_QOS_EDCA_AC_BE; ac_index < SME_QOS_EDCA_AC_MAX; ac_index++) |
| { |
| pEntry = sme_QosFindInFlowList(search_key1); |
| if(pEntry) |
| { |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| if(flow_info->ac_type == ac) |
| { |
| pACInfo->hoRenewal = flow_info->hoRenewal; |
| break; |
| } |
| } |
| } |
| //notify HDD the success for the requested flow |
| //notify all the other flows running on the AC that QoS got modified |
| if(!HAL_STATUS_SUCCESS(sme_QosFindAllInFlowList(pMac, search_key, sme_QosReassocSuccessEvFnp))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: no match found for ac = %d", |
| __func__, __LINE__, |
| search_key.key.ac_type); |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| pACInfo->hoRenewal = VOS_FALSE; |
| vos_mem_zero(&pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0], |
| sizeof(sme_QosWmmTspecInfo)); |
| } |
| status = eHAL_STATUS_SUCCESS; |
| break; |
| case SME_QOS_INIT: |
| case SME_QOS_CLOSED: |
| //NOP |
| status = eHAL_STATUS_SUCCESS; |
| break; |
| case SME_QOS_LINK_UP: |
| case SME_QOS_REQUESTED: |
| case SME_QOS_QOS_ON: |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d AC %d is in wrong state %d", |
| __func__, __LINE__, |
| sessionId, ac, pACInfo->curr_state); |
| VOS_ASSERT(0); |
| break; |
| } |
| } |
| (void)sme_QosProcessBufferedCmd(sessionId); |
| return status; |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessReassocFailureEv() - Function to process the |
| SME_QOS_CSR_REASSOC_FAILURE event indication from CSR |
| \param pEvent_info - Pointer to relevant info from CSR. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessReassocFailureEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosEdcaAcType ac; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d", |
| __func__, __LINE__, |
| sessionId); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| /* Our pending reassociation has completed |
| we can allow power save */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_HANDOFF: |
| sme_QosStateTransition(sessionId, ac, SME_QOS_INIT); |
| if(pACInfo->reassoc_pending) |
| { |
| pACInfo->reassoc_pending = VOS_FALSE; |
| } |
| vos_mem_zero(&pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0], |
| sizeof(sme_QosWmmTspecInfo)); |
| vos_mem_zero(&pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0], |
| sizeof(sme_QosWmmTspecInfo)); |
| vos_mem_zero(&pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_1], |
| sizeof(sme_QosWmmTspecInfo)); |
| vos_mem_zero(&pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_1], |
| sizeof(sme_QosWmmTspecInfo)); |
| pACInfo->tspec_mask_status = SME_QOS_TSPEC_MASK_CLEAR; |
| pACInfo->tspec_pending = 0; |
| pACInfo->num_flows[SME_QOS_TSPEC_INDEX_0] = 0; |
| pACInfo->num_flows[SME_QOS_TSPEC_INDEX_1] = 0; |
| break; |
| case SME_QOS_INIT: |
| case SME_QOS_CLOSED: |
| //NOP |
| break; |
| case SME_QOS_LINK_UP: |
| case SME_QOS_REQUESTED: |
| case SME_QOS_QOS_ON: |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d AC %d is in wrong state %d", |
| __func__, __LINE__, |
| sessionId, ac, pACInfo->curr_state); |
| VOS_ASSERT(0); |
| break; |
| } |
| } |
| //need to clean up flows |
| sme_QosDeleteExistingFlows(pMac, sessionId); |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessHandoffAssocReqEv() - Function to process the |
| SME_QOS_CSR_HANDOFF_ASSOC_REQ event indication from CSR |
| \param pEvent_info - Pointer to relevant info from CSR. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessHandoffAssocReqEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| v_U8_t ac; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d", |
| __func__, __LINE__, |
| sessionId); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_LINK_UP: |
| case SME_QOS_REQUESTED: |
| case SME_QOS_QOS_ON: |
| sme_QosStateTransition(sessionId, ac, SME_QOS_HANDOFF); |
| break; |
| case SME_QOS_HANDOFF: |
| //print error msg |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| if(pSession->ftHandoffInProgress) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| "%s: %d: SME_QOS_CSR_HANDOFF_ASSOC_REQ received in " |
| "SME_QOS_HANDOFF state with FT in progress" |
| , __func__, __LINE__); |
| break; |
| } |
| #endif |
| |
| case SME_QOS_CLOSED: |
| case SME_QOS_INIT: |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d AC %d is in wrong state %d", |
| __func__, __LINE__, |
| sessionId, ac, pACInfo->curr_state); |
| VOS_ASSERT(0); |
| break; |
| } |
| } |
| |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| if (csrRoamIs11rAssoc(pMac, sessionId)) { |
| /* Need not check here if it is LFR3.0 roaming, |
| * since ftHandoffInProgress will be true if it |
| * is 11r assoc even with LFR2.0 */ |
| pSession->ftHandoffInProgress = VOS_TRUE; |
| } |
| #endif |
| |
| // If FT handoff is in progress, legacy handoff need not be enabled |
| if (!pSession->ftHandoffInProgress) { |
| pSession->handoffRequested = VOS_TRUE; |
| } |
| // this session no longer needs UAPSD |
| pSession->apsdMask = 0; |
| // do any sessions still require UAPSD? |
| if(!pMac->psOffloadEnabled) |
| { |
| if (!sme_QosIsUapsdActive()) |
| { |
| // No sessions require UAPSD so turn it off |
| // (really don't care when PMC stops it) |
| (void)pmcStopUapsd(pMac); |
| } |
| } |
| else |
| { |
| (void)pmcOffloadStopUapsd(pMac, sessionId); |
| } |
| pSession->uapsdAlreadyRequested = VOS_FALSE; |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessHandoffSuccessEv() - Function to process the |
| SME_QOS_CSR_HANDOFF_COMPLETE event indication from CSR |
| \param pEvent_info - Pointer to relevant info from CSR. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessHandoffSuccessEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| v_U8_t ac; |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d", |
| __func__, __LINE__, |
| sessionId); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| //go back to original state before handoff |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_HANDOFF: |
| sme_QosStateTransition(sessionId, ac, pACInfo->prev_state); |
| //we will retry for the requested flow(s) with the new AP |
| if(SME_QOS_REQUESTED == pACInfo->curr_state) |
| { |
| pACInfo->curr_state = SME_QOS_LINK_UP; |
| } |
| status = eHAL_STATUS_SUCCESS; |
| break; |
| // FT logic, has already moved it to QOS_REQUESTED state during the |
| // reassoc request event, which would include the Qos (TSPEC) params |
| // in the reassoc req frame |
| case SME_QOS_REQUESTED: |
| break; |
| case SME_QOS_INIT: |
| case SME_QOS_CLOSED: |
| case SME_QOS_LINK_UP: |
| case SME_QOS_QOS_ON: |
| default: |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| /* In case of 11r - RIC, we request QoS and Hand-off at the same time hence the |
| state may be SME_QOS_REQUESTED */ |
| if( pSession->ftHandoffInProgress ) |
| break; |
| #endif |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d AC %d is in wrong state %d", |
| __func__, __LINE__, |
| sessionId, ac, pACInfo->curr_state); |
| VOS_ASSERT(0); |
| break; |
| } |
| } |
| return status; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessHandoffFailureEv() - Function to process the |
| SME_QOS_CSR_HANDOFF_FAILURE event indication from CSR |
| \param pEvent_info - Pointer to relevant info from CSR. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessHandoffFailureEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| v_U8_t ac; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d", |
| __func__, __LINE__, |
| sessionId); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_HANDOFF: |
| sme_QosStateTransition(sessionId, ac, SME_QOS_INIT); |
| //need to clean up flows: TODO |
| vos_mem_zero(&pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0], |
| sizeof(sme_QosWmmTspecInfo)); |
| vos_mem_zero(&pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0], |
| sizeof(sme_QosWmmTspecInfo)); |
| vos_mem_zero(&pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_1], |
| sizeof(sme_QosWmmTspecInfo)); |
| vos_mem_zero(&pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_1], |
| sizeof(sme_QosWmmTspecInfo)); |
| pACInfo->tspec_mask_status = SME_QOS_TSPEC_MASK_CLEAR; |
| pACInfo->tspec_pending = 0; |
| pACInfo->num_flows[SME_QOS_TSPEC_INDEX_0] = 0; |
| pACInfo->num_flows[SME_QOS_TSPEC_INDEX_1] = 0; |
| break; |
| case SME_QOS_INIT: |
| case SME_QOS_CLOSED: |
| case SME_QOS_LINK_UP: |
| case SME_QOS_REQUESTED: |
| case SME_QOS_QOS_ON: |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d AC %d is in wrong state %d", |
| __func__, __LINE__, |
| sessionId, ac, pACInfo->curr_state); |
| VOS_ASSERT(0); |
| break; |
| } |
| } |
| //no longer in handoff |
| pSession->handoffRequested = VOS_FALSE; |
| //clean up the assoc info |
| if(pSession->assocInfo.pBssDesc) |
| { |
| vos_mem_free(pSession->assocInfo.pBssDesc); |
| pSession->assocInfo.pBssDesc = NULL; |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessDisconnectEv() - Function to process the |
| SME_QOS_CSR_DISCONNECT_REQ or SME_QOS_CSR_DISCONNECT_IND event indication |
| from CSR |
| \param pEvent_info - Pointer to relevant info from CSR. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessDisconnectEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info) |
| { |
| sme_QosSessionInfo *pSession; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d", |
| __func__, __LINE__, |
| sessionId); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| if((pSession->handoffRequested) |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| /* In case of 11r - RIC, we request QoS and Hand-off at the same time hence the |
| state may be SME_QOS_REQUESTED */ |
| && !pSession->ftHandoffInProgress |
| #endif |
| ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: no need for state transition, should " |
| "already be in handoff state", |
| __func__, __LINE__); |
| if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[3].curr_state != SME_QOS_HANDOFF)) |
| { |
| VOS_ASSERT(0); |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| sme_QosInitACs(pMac, sessionId); |
| // this session doesn't require UAPSD |
| pSession->apsdMask = 0; |
| |
| if(!pMac->psOffloadEnabled) |
| { |
| // do any sessions still require UAPSD? |
| if (!sme_QosIsUapsdActive()) |
| { |
| // No sessions require UAPSD so turn it off |
| // (really don't care when PMC stops it) |
| (void)pmcStopUapsd(pMac); |
| } |
| } |
| else |
| { |
| (void)pmcOffloadStopUapsd(pMac, sessionId); |
| } |
| |
| pSession->uapsdAlreadyRequested = VOS_FALSE; |
| pSession->handoffRequested = VOS_FALSE; |
| pSession->readyForPowerSave = VOS_TRUE; |
| pSession->roamID = 0; |
| //need to clean up buffered req |
| sme_QosDeleteBufferedRequests(pMac, sessionId); |
| //need to clean up flows |
| sme_QosDeleteExistingFlows(pMac, sessionId); |
| //clean up the assoc info |
| if(pSession->assocInfo.pBssDesc) |
| { |
| vos_mem_free(pSession->assocInfo.pBssDesc); |
| pSession->assocInfo.pBssDesc = NULL; |
| } |
| sme_QosCb.sessionInfo[sessionId].sessionActive = VOS_FALSE; |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessJoinReqEv() - Function to process the |
| SME_QOS_CSR_JOIN_REQ event indication from CSR |
| \param pEvent_info - Pointer to relevant info from CSR. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessJoinReqEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosEdcaAcType ac; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d", |
| __func__, __LINE__, |
| sessionId); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| if(pSession->handoffRequested) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: no need for state transition, should " |
| "already be in handoff state", |
| __func__, __LINE__); |
| if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) || |
| (pSession->ac_info[3].curr_state != SME_QOS_HANDOFF)) |
| { |
| // just print |
| VOS_ASSERT(0); |
| } |
| |
| //buffer the existing flows to be renewed after handoff is done |
| sme_QosBufferExistingFlows(pMac, sessionId); |
| //clean up the control block partially for handoff |
| sme_QosCleanupCtrlBlkForHandoff(pMac, sessionId); |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| sme_QosStateTransition(sessionId, ac, SME_QOS_INIT); |
| } |
| //clean up the assoc info if already set |
| if(pSession->assocInfo.pBssDesc) |
| { |
| vos_mem_free(pSession->assocInfo.pBssDesc); |
| pSession->assocInfo.pBssDesc = NULL; |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessPreauthSuccessInd() - Function to process the |
| SME_QOS_CSR_PREAUTH_SUCCESS_IND event indication from CSR |
| |
| \param pEvent_info - Pointer to relevant info from CSR. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessPreauthSuccessInd(tpAniSirGlobal pMac, |
| v_U8_t sessionId, void * pEvent_info) |
| { |
| sme_QosSessionInfo *pSession; |
| tCsrRoamSession *pSmeSession = CSR_GET_SESSION( pMac, sessionId ); |
| sme_QosACInfo *pACInfo; |
| v_U8_t ac; |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| FL("invoked on SME session %d"), sessionId); |
| |
| if (NULL == pSmeSession) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("pSmeSession is NULL")); |
| return eHAL_STATUS_INVALID_PARAMETER; |
| } |
| |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_LINK_UP: |
| case SME_QOS_REQUESTED: |
| case SME_QOS_QOS_ON: |
| sme_QosStateTransition(sessionId, ac, SME_QOS_HANDOFF); |
| break; |
| case SME_QOS_HANDOFF: |
| //print error msg |
| case SME_QOS_CLOSED: |
| case SME_QOS_INIT: |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d AC %d is in wrong state %d", |
| __func__, __LINE__, |
| sessionId, ac, pACInfo->curr_state); |
| VOS_ASSERT(0); |
| break; |
| } |
| } |
| |
| pSession->ftHandoffInProgress = VOS_TRUE; |
| |
| // Check if its a 11R roaming before preparing the RIC IEs |
| if (csrRoamIs11rAssoc(pMac, sessionId)) { |
| v_U16_t ricOffset = 0; |
| v_U32_t ricIELength = 0; |
| v_U8_t *ricIE; |
| v_U8_t tspec_mask_status = 0; |
| v_U8_t tspec_pending_status = 0; |
| |
| /* Data is accessed from saved PreAuth Rsp */ |
| if (NULL == pSmeSession->ftSmeContext.psavedFTPreAuthRsp) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| FL("psavedFTPreAuthRsp is NULL")); |
| return eHAL_STATUS_INVALID_PARAMETER; |
| } |
| |
| /* Any Block Ack info there, should have been already filled by PE and |
| present in this buffer and the ric_ies_length should contain the |
| length of the whole RIC IEs. Filling of TSPEC info should start |
| from this length */ |
| ricIE = pSmeSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies; |
| ricOffset = |
| pSmeSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies_length; |
| |
| /* Now we have to process the currentTspeInfo inside this session and |
| create the RIC IEs */ |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| volatile v_U8_t tspec_index = 0; |
| ricIELength = 0; |
| pACInfo = &pSession->ac_info[ac]; |
| tspec_pending_status = pACInfo->tspec_pending; |
| tspec_mask_status = pACInfo->tspec_mask_status; |
| vos_mem_zero(pACInfo->ricIdentifier, SME_QOS_TSPEC_INDEX_MAX); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| FL("AC %d ==> TSPEC status = %d, tspec pending = %d"), |
| ac, tspec_mask_status, tspec_pending_status); |
| |
| do |
| { |
| if (tspec_mask_status & 0x1) |
| { |
| /* If a tspec status is pending, take requested_QoSInfo for |
| RIC request, else use curr_QoSInfo for the RIC request */ |
| if (tspec_pending_status & 0x1) |
| { |
| status = sme_QosCreateTspecRICIE(pMac, |
| &pACInfo->requested_QoSInfo[tspec_index], |
| ricIE + ricOffset, &ricIELength, |
| &pACInfo->ricIdentifier[tspec_index]); |
| } |
| else |
| { |
| status = sme_QosCreateTspecRICIE(pMac, |
| &pACInfo->curr_QoSInfo[tspec_index], |
| ricIE + ricOffset, &ricIELength, |
| &pACInfo->ricIdentifier[tspec_index]); |
| } |
| } |
| ricOffset += ricIELength; |
| pSmeSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies_length += |
| ricIELength; |
| |
| tspec_mask_status >>= 1; |
| tspec_pending_status >>= 1; |
| tspec_index++; |
| } while (tspec_mask_status); |
| } |
| } |
| return status; |
| } |
| |
| #endif |
| |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessAddTsFailureRsp() - Function to process the |
| Addts request failure response came from PE |
| |
| We will notify HDD only for the requested Flow, other Flows running on the AC |
| stay intact |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pRsp - Pointer to the addts response structure came from PE. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessAddTsFailureRsp(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| tSirAddtsRspInfo * pRsp) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosEdcaAcType ac; |
| sme_QosSearchInfo search_key; |
| v_U8_t tspec_pending; |
| sme_QosWmmUpType up = (sme_QosWmmUpType)pRsp->tspec.tsinfo.traffic.userPrio; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d for UP %d", |
| __func__, __LINE__, |
| sessionId, up); |
| ac = sme_QosUpToAc(up); |
| if(SME_QOS_EDCA_AC_MAX == ac) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: invalid AC %d from UP %d", |
| __func__, __LINE__, ac, up); |
| return eHAL_STATUS_FAILURE; |
| } |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| // is there a TSPEC request pending on this AC? |
| tspec_pending = pACInfo->tspec_pending; |
| if(!tspec_pending) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d an AddTS is not pending on AC %d", |
| __func__, __LINE__, |
| sessionId, ac); |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| //set the key type & the key to be searched in the Flow List |
| search_key.key.ac_type = ac; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; |
| search_key.sessionId = sessionId; |
| if(!HAL_STATUS_SUCCESS(sme_QosFindAllInFlowList(pMac, search_key, sme_QosAddTsFailureFnp))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d no match found for ac = %d", |
| __func__, __LINE__, |
| sessionId, search_key.key.ac_type); |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| vos_mem_zero(&pACInfo->requested_QoSInfo[tspec_pending - 1], |
| sizeof(sme_QosWmmTspecInfo)); |
| |
| if((!pACInfo->num_flows[0])&& |
| (!pACInfo->num_flows[1])) |
| { |
| pACInfo->tspec_mask_status &= SME_QOS_TSPEC_MASK_BIT_1_2_SET & |
| (~pACInfo->tspec_pending); |
| sme_QosStateTransition(sessionId, ac, SME_QOS_LINK_UP); |
| } |
| else |
| { |
| sme_QosStateTransition(sessionId, ac, SME_QOS_QOS_ON); |
| } |
| pACInfo->tspec_pending = 0; |
| |
| (void)sme_QosProcessBufferedCmd(sessionId); |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosUpdateTspecMask() - Utility function to update the tspec. |
| Typical usage while aggregating unidirectional flows into a bi-directional |
| flow on AC which is running multiple flows |
| |
| \param sessionId - Session upon which the TSPEC is being updated |
| \param ac - Enumeration of the various EDCA Access Categories. |
| \param old_tspec_mask - on which tspec per AC, the update is requested |
| \param new_tspec_mask - tspec to be set for this AC |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| static eHalStatus sme_QosUpdateTspecMask(v_U8_t sessionId, |
| sme_QosSearchInfo search_key, |
| v_U8_t new_tspec_mask) |
| { |
| tListElem *pEntry= NULL, *pNextEntry = NULL; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d for AC %d TSPEC %d", |
| __func__, __LINE__, |
| sessionId, search_key.key.ac_type, new_tspec_mask); |
| |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| |
| if (search_key.key.ac_type < SME_QOS_EDCA_AC_MAX) |
| { |
| pACInfo = &pSession->ac_info[search_key.key.ac_type]; |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Exceeded the array bounds of pSession->ac_info", |
| __func__, __LINE__); |
| VOS_ASSERT (0); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE ); |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Flow List empty, nothing to update", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| |
| while( pEntry ) |
| { |
| pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE ); |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| |
| if(search_key.sessionId == flow_info->sessionId) |
| { |
| if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_4) |
| { |
| if((search_key.key.ac_type == flow_info->ac_type) && |
| (search_key.direction == flow_info->QoSInfo.ts_info.direction)) |
| { |
| //msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Flow %d matches", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| pACInfo->num_flows[flow_info->tspec_mask - 1]--; |
| pACInfo->num_flows[new_tspec_mask - 1]++; |
| flow_info->tspec_mask = new_tspec_mask; |
| } |
| } |
| else if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_5) |
| { |
| if((search_key.key.ac_type == flow_info->ac_type) && |
| (search_key.tspec_mask == flow_info->tspec_mask)) |
| { |
| //msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Flow %d matches", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| pACInfo->num_flows[flow_info->tspec_mask - 1]--; |
| pACInfo->num_flows[new_tspec_mask - 1]++; |
| flow_info->tspec_mask = new_tspec_mask; |
| } |
| } |
| } |
| |
| pEntry = pNextEntry; |
| } |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessAddTsSuccessRsp() - Function to process the |
| Addts request success response came from PE |
| |
| We will notify HDD with addts success for the requested Flow, & for other |
| Flows running on the AC we will send an addts modify status |
| |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pRsp - Pointer to the addts response structure came from PE. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessAddTsSuccessRsp(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| tSirAddtsRspInfo * pRsp) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosEdcaAcType ac, ac_index; |
| sme_QosSearchInfo search_key; |
| sme_QosSearchInfo search_key1; |
| v_U8_t tspec_pending; |
| tListElem *pEntry= NULL; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| sme_QosWmmUpType up = (sme_QosWmmUpType)pRsp->tspec.tsinfo.traffic.userPrio; |
| #ifdef FEATURE_WLAN_DIAG_SUPPORT |
| WLAN_VOS_DIAG_EVENT_DEF(qos, vos_event_wlan_qos_payload_type); |
| vos_log_qos_tspec_pkt_type *log_ptr = NULL; |
| #endif //FEATURE_WLAN_DIAG_SUPPORT |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d for UP %d", |
| __func__, __LINE__, |
| sessionId, up); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| ac = sme_QosUpToAc(up); |
| if(SME_QOS_EDCA_AC_MAX == ac) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: invalid AC %d from UP %d", |
| __func__, __LINE__, ac, up); |
| return eHAL_STATUS_FAILURE; |
| } |
| pACInfo = &pSession->ac_info[ac]; |
| // is there a TSPEC request pending on this AC? |
| tspec_pending = pACInfo->tspec_pending; |
| if(!tspec_pending) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d an AddTS is not pending on AC %d", |
| __func__, __LINE__, |
| sessionId, ac); |
| return eHAL_STATUS_FAILURE; |
| } |
| //App is looking for APSD or the App which was looking for APSD has been |
| //released, so STA re-negotiated with AP |
| if(pACInfo->requested_QoSInfo[tspec_pending - 1].ts_info.psb) |
| { |
| //update the session's apsd mask |
| pSession->apsdMask |= 1 << (SME_QOS_EDCA_AC_VO - ac); |
| } |
| else |
| { |
| if(((SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~tspec_pending) > 0) && |
| ((SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~tspec_pending) <= |
| SME_QOS_TSPEC_INDEX_MAX)) |
| { |
| if(!pACInfo->requested_QoSInfo |
| [(SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~tspec_pending) - 1].ts_info.psb) |
| { |
| //update the session's apsd mask |
| pSession->apsdMask &= ~(1 << (SME_QOS_EDCA_AC_VO - ac)); |
| } |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Exceeded the array bounds of pACInfo->requested_QosInfo", |
| __func__, __LINE__); |
| VOS_ASSERT (0); |
| return eHAL_STATUS_FAILURE; |
| } |
| } |
| |
| pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.burst_size_defn = |
| pRsp->tspec.tsinfo.traffic.burstSizeDefn; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.ack_policy = |
| pRsp->tspec.tsinfo.traffic.ackPolicy; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.up = |
| pRsp->tspec.tsinfo.traffic.userPrio; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.psb = |
| pRsp->tspec.tsinfo.traffic.psb; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.direction = |
| pRsp->tspec.tsinfo.traffic.direction; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.tid = |
| pRsp->tspec.tsinfo.traffic.tsid; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].nominal_msdu_size = |
| pRsp->tspec.nomMsduSz; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].maximum_msdu_size = |
| pRsp->tspec.maxMsduSz; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].min_service_interval = |
| pRsp->tspec.minSvcInterval; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].max_service_interval = |
| pRsp->tspec.maxSvcInterval; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].inactivity_interval = |
| pRsp->tspec.inactInterval; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].suspension_interval = |
| pRsp->tspec.suspendInterval; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].svc_start_time = |
| pRsp->tspec.svcStartTime; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].min_data_rate = |
| pRsp->tspec.minDataRate; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].mean_data_rate = |
| pRsp->tspec.meanDataRate; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].peak_data_rate = |
| pRsp->tspec.peakDataRate; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].max_burst_size = |
| pRsp->tspec.maxBurstSz; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].delay_bound = |
| pRsp->tspec.delayBound; |
| |
| pACInfo->curr_QoSInfo[tspec_pending - 1].min_phy_rate = |
| pRsp->tspec.minPhyRate; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].surplus_bw_allowance = |
| pRsp->tspec.surplusBw; |
| pACInfo->curr_QoSInfo[tspec_pending - 1].medium_time = |
| pRsp->tspec.mediumTime; |
| |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d AddTspec Medium Time %d", |
| __func__, __LINE__, |
| sessionId, pRsp->tspec.mediumTime); |
| |
| /* Check if the current flow is for bi-directional. If so, update the number of flows |
| * to reflect that all flows are aggregated into tspec index 0. */ |
| if((pACInfo->curr_QoSInfo[pACInfo->tspec_pending - 1].ts_info.direction == SME_QOS_WMM_TS_DIR_BOTH) && |
| (pACInfo->num_flows[SME_QOS_TSPEC_INDEX_1] > 0)) |
| { |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| /* update tspec_mask for all the flows having SME_QOS_TSPEC_MASK_BIT_2_SET to SME_QOS_TSPEC_MASK_BIT_1_SET */ |
| search_key.key.ac_type = ac; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_5; |
| search_key.sessionId = sessionId; |
| search_key.tspec_mask = SME_QOS_TSPEC_MASK_BIT_2_SET; |
| sme_QosUpdateTspecMask(sessionId, search_key, SME_QOS_TSPEC_MASK_BIT_1_SET); |
| } |
| |
| vos_mem_zero(&search_key1, sizeof(sme_QosSearchInfo)); |
| //set the horenewal field in control block if needed |
| search_key1.index = SME_QOS_SEARCH_KEY_INDEX_3; |
| search_key1.key.reason = SME_QOS_REASON_SETUP; |
| search_key1.sessionId = sessionId; |
| for(ac_index = SME_QOS_EDCA_AC_BE; ac_index < SME_QOS_EDCA_AC_MAX; ac_index++) |
| { |
| pEntry = sme_QosFindInFlowList(search_key1); |
| if(pEntry) |
| { |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| if(flow_info->ac_type == ac) |
| { |
| pACInfo->hoRenewal = flow_info->hoRenewal; |
| break; |
| } |
| } |
| } |
| vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo)); |
| //set the key type & the key to be searched in the Flow List |
| search_key.key.ac_type = ac; |
| search_key.index = SME_QOS_SEARCH_KEY_INDEX_2; |
| search_key.sessionId = sessionId; |
| //notify HDD the success for the requested flow |
| //notify all the other flows running on the AC that QoS got modified |
| if(!HAL_STATUS_SUCCESS(sme_QosFindAllInFlowList(pMac, search_key, sme_QosAddTsSuccessFnp))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d no match found for ac %d", |
| __func__, __LINE__, |
| sessionId, search_key.key.ac_type); |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| pACInfo->hoRenewal = VOS_FALSE; |
| vos_mem_zero(&pACInfo->requested_QoSInfo[tspec_pending - 1], |
| sizeof(sme_QosWmmTspecInfo)); |
| //event: EVENT_WLAN_QOS |
| #ifdef FEATURE_WLAN_DIAG_SUPPORT |
| qos.eventId = SME_QOS_DIAG_ADDTS_RSP; |
| qos.reasonCode = SME_QOS_DIAG_ADDTS_ADMISSION_ACCEPTED; |
| WLAN_VOS_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS); |
| WLAN_VOS_DIAG_LOG_ALLOC(log_ptr, vos_log_qos_tspec_pkt_type, LOG_WLAN_QOS_TSPEC_C); |
| if(log_ptr) |
| { |
| log_ptr->delay_bound = pACInfo->curr_QoSInfo[tspec_pending - 1].delay_bound; |
| log_ptr->inactivity_interval = pACInfo->curr_QoSInfo[tspec_pending - 1].inactivity_interval; |
| log_ptr->max_burst_size = pACInfo->curr_QoSInfo[tspec_pending - 1].max_burst_size; |
| log_ptr->max_service_interval = pACInfo->curr_QoSInfo[tspec_pending - 1].max_service_interval; |
| log_ptr->maximum_msdu_size = pACInfo->curr_QoSInfo[tspec_pending - 1].maximum_msdu_size; |
| log_ptr->mean_data_rate = pACInfo->curr_QoSInfo[tspec_pending - 1].mean_data_rate; |
| log_ptr->medium_time = pACInfo->curr_QoSInfo[tspec_pending - 1].medium_time; |
| log_ptr->min_data_rate = pACInfo->curr_QoSInfo[tspec_pending - 1].min_data_rate; |
| log_ptr->min_phy_rate = pACInfo->curr_QoSInfo[tspec_pending - 1].min_phy_rate; |
| log_ptr->min_service_interval = pACInfo->curr_QoSInfo[tspec_pending - 1].min_service_interval; |
| log_ptr->nominal_msdu_size = pACInfo->curr_QoSInfo[tspec_pending - 1].nominal_msdu_size; |
| log_ptr->peak_data_rate = pACInfo->curr_QoSInfo[tspec_pending - 1].peak_data_rate; |
| log_ptr->surplus_bw_allowance = pACInfo->curr_QoSInfo[tspec_pending - 1].surplus_bw_allowance; |
| log_ptr->suspension_interval = pACInfo->curr_QoSInfo[tspec_pending - 1].suspension_interval; |
| log_ptr->svc_start_time = pACInfo->curr_QoSInfo[tspec_pending - 1].svc_start_time; |
| log_ptr->tsinfo[0] = pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.direction << 5 | |
| pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.tid << 1; |
| log_ptr->tsinfo[1] = pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.up << 11 | |
| pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.psb << 10; |
| log_ptr->tsinfo[2] = 0; |
| } |
| WLAN_VOS_DIAG_LOG_REPORT(log_ptr); |
| #endif //FEATURE_WLAN_DIAG_SUPPORT |
| #if defined(FEATURE_WLAN_ESE) && !defined(FEATURE_WLAN_ESE_UPLOAD) |
| if (ac == SME_QOS_EDCA_AC_VO) |
| { |
| // Indicate to neighbor roam logic of the new required VO |
| // ac bandwidth requirement. |
| csrNeighborRoamIndicateVoiceBW( pMac, pACInfo->curr_QoSInfo[tspec_pending - 1].peak_data_rate, TRUE ); |
| } |
| #endif |
| pACInfo->tspec_pending = 0; |
| |
| sme_QosStateTransition(sessionId, ac, SME_QOS_QOS_ON); |
| |
| |
| (void)sme_QosProcessBufferedCmd(sessionId); |
| return eHAL_STATUS_SUCCESS; |
| |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosAggregateParams() - Utility function to increment the TSPEC |
| params per AC. Typical usage while using flow aggregation or deletion of flows |
| |
| \param pInput_Tspec_Info - Pointer to sme_QosWmmTspecInfo which contains the |
| WMM TSPEC related info with which pCurrent_Tspec_Info will be updated |
| \param pCurrent_Tspec_Info - Pointer to sme_QosWmmTspecInfo which contains |
| current the WMM TSPEC related info |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosAggregateParams( |
| sme_QosWmmTspecInfo * pInput_Tspec_Info, |
| sme_QosWmmTspecInfo * pCurrent_Tspec_Info, |
| sme_QosWmmTspecInfo * pUpdated_Tspec_Info) |
| { |
| sme_QosWmmTspecInfo TspecInfo; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked", |
| __func__, __LINE__); |
| if(!pInput_Tspec_Info) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: input is NULL, nothing to aggregate", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| if(!pCurrent_Tspec_Info) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Current is NULL, can't aggregate", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| vos_mem_copy(&TspecInfo, pCurrent_Tspec_Info, |
| sizeof(sme_QosWmmTspecInfo)); |
| TspecInfo.ts_info.psb = pInput_Tspec_Info->ts_info.psb; |
| /*------------------------------------------------------------------------- |
| APSD preference is only meaningful if service interval was set by app |
| -------------------------------------------------------------------------*/ |
| if(pCurrent_Tspec_Info->min_service_interval && |
| pInput_Tspec_Info->min_service_interval && |
| (pCurrent_Tspec_Info->ts_info.direction != |
| pInput_Tspec_Info->ts_info.direction)) |
| { |
| TspecInfo.min_service_interval = VOS_MIN( |
| pCurrent_Tspec_Info->min_service_interval, |
| pInput_Tspec_Info->min_service_interval); |
| } |
| else if(pInput_Tspec_Info->min_service_interval) |
| { |
| TspecInfo.min_service_interval = pInput_Tspec_Info->min_service_interval; |
| } |
| if(pCurrent_Tspec_Info->max_service_interval && |
| pInput_Tspec_Info->max_service_interval && |
| (pCurrent_Tspec_Info->ts_info.direction != |
| pInput_Tspec_Info->ts_info.direction)) |
| { |
| TspecInfo.max_service_interval = VOS_MIN( |
| pCurrent_Tspec_Info->max_service_interval, |
| pInput_Tspec_Info->max_service_interval); |
| } |
| else |
| { |
| TspecInfo.max_service_interval = pInput_Tspec_Info->max_service_interval; |
| } |
| /*------------------------------------------------------------------------- |
| If directions don't match, it must necessarily be both uplink and |
| downlink |
| -------------------------------------------------------------------------*/ |
| if(pCurrent_Tspec_Info->ts_info.direction != |
| pInput_Tspec_Info->ts_info.direction) |
| { |
| TspecInfo.ts_info.direction = pInput_Tspec_Info->ts_info.direction; |
| } |
| /*------------------------------------------------------------------------- |
| Max MSDU size : these sizes are `maxed' |
| -------------------------------------------------------------------------*/ |
| TspecInfo.maximum_msdu_size = VOS_MAX(pCurrent_Tspec_Info->maximum_msdu_size, |
| pInput_Tspec_Info->maximum_msdu_size); |
| |
| /*------------------------------------------------------------------------- |
| Inactivity interval : these sizes are `maxed' |
| -------------------------------------------------------------------------*/ |
| TspecInfo.inactivity_interval = VOS_MAX(pCurrent_Tspec_Info->inactivity_interval, |
| pInput_Tspec_Info->inactivity_interval); |
| |
| /*------------------------------------------------------------------------- |
| Delay bounds: min of all values |
| Check on 0: if 0, it means initial value since delay can never be 0!! |
| -------------------------------------------------------------------------*/ |
| if(pCurrent_Tspec_Info->delay_bound) |
| { |
| TspecInfo.delay_bound = VOS_MIN(pCurrent_Tspec_Info->delay_bound, |
| pInput_Tspec_Info->delay_bound); |
| } |
| else |
| { |
| TspecInfo.delay_bound = pInput_Tspec_Info->delay_bound; |
| } |
| TspecInfo.max_burst_size = VOS_MAX(pCurrent_Tspec_Info->max_burst_size, |
| pInput_Tspec_Info->max_burst_size); |
| |
| /*------------------------------------------------------------------------- |
| Nominal MSDU size also has a fixed bit that needs to be `handled' before |
| aggregation |
| This can be handled only if previous size is the same as new or both have |
| the fixed bit set |
| These sizes are not added: but `maxed' |
| -------------------------------------------------------------------------*/ |
| TspecInfo.nominal_msdu_size = VOS_MAX( |
| pCurrent_Tspec_Info->nominal_msdu_size & ~SME_QOS_16BIT_MSB, |
| pInput_Tspec_Info->nominal_msdu_size & ~SME_QOS_16BIT_MSB); |
| |
| if( ((pCurrent_Tspec_Info->nominal_msdu_size == 0) || |
| (pCurrent_Tspec_Info->nominal_msdu_size & SME_QOS_16BIT_MSB)) && |
| ((pInput_Tspec_Info->nominal_msdu_size == 0) || |
| (pInput_Tspec_Info->nominal_msdu_size & SME_QOS_16BIT_MSB))) |
| { |
| TspecInfo.nominal_msdu_size |= SME_QOS_16BIT_MSB; |
| } |
| |
| /*------------------------------------------------------------------------- |
| Data rates: |
| Add up the rates for aggregation |
| -------------------------------------------------------------------------*/ |
| SME_QOS_BOUNDED_U32_ADD_Y_TO_X( TspecInfo.peak_data_rate, |
| pInput_Tspec_Info->peak_data_rate ); |
| SME_QOS_BOUNDED_U32_ADD_Y_TO_X( TspecInfo.min_data_rate, |
| pInput_Tspec_Info->min_data_rate ); |
| /* mean data rate = peak data rate: aggregate to be flexible on apps */ |
| SME_QOS_BOUNDED_U32_ADD_Y_TO_X( TspecInfo.mean_data_rate, |
| pInput_Tspec_Info->mean_data_rate ); |
| |
| /*------------------------------------------------------------------------- |
| Suspension interval : this is set to the inactivity interval since per |
| spec it is less than or equal to inactivity interval |
| This is not provided by app since we currently don't support the HCCA |
| mode of operation |
| Currently set it to 0 to avoid confusion: Cisco ESE needs ~0; spec |
| requires inactivity interval to be > suspension interval: this could |
| be tricky! |
| -------------------------------------------------------------------------*/ |
| TspecInfo.suspension_interval = pInput_Tspec_Info->suspension_interval; |
| /*------------------------------------------------------------------------- |
| Remaining parameters do not come from app as they are very WLAN |
| air interface specific |
| Set meaningful values here |
| -------------------------------------------------------------------------*/ |
| TspecInfo.medium_time = 0; /* per WMM spec */ |
| TspecInfo.min_phy_rate = SME_QOS_MIN_PHY_RATE; |
| TspecInfo.svc_start_time = 0; /* arbitrary */ |
| TspecInfo.surplus_bw_allowance += pInput_Tspec_Info->surplus_bw_allowance; |
| if(TspecInfo.surplus_bw_allowance > SME_QOS_SURPLUS_BW_ALLOWANCE) |
| { |
| TspecInfo.surplus_bw_allowance = SME_QOS_SURPLUS_BW_ALLOWANCE; |
| } |
| /* Set ack_policy to block ack even if one stream requests block ack policy */ |
| if((pInput_Tspec_Info->ts_info.ack_policy == SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK) || |
| (pCurrent_Tspec_Info->ts_info.ack_policy == SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK)) |
| { |
| TspecInfo.ts_info.ack_policy = SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK; |
| } |
| |
| if(pInput_Tspec_Info->ts_info.burst_size_defn || pCurrent_Tspec_Info->ts_info.burst_size_defn ) |
| { |
| TspecInfo.ts_info.burst_size_defn = 1; |
| } |
| if(pUpdated_Tspec_Info) |
| { |
| vos_mem_copy(pUpdated_Tspec_Info, &TspecInfo, |
| sizeof(sme_QosWmmTspecInfo)); |
| } |
| else |
| { |
| vos_mem_copy(pCurrent_Tspec_Info, &TspecInfo, |
| sizeof(sme_QosWmmTspecInfo)); |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosUpdateParams() - Utility function to update the TSPEC |
| params per AC. Typical usage while deleting flows on AC which is running |
| multiple flows |
| |
| \param sessionId - Session upon which the TSPEC is being updated |
| \param ac - Enumeration of the various EDCA Access Categories. |
| \param tspec_mask - on which tspec per AC, the update is requested |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| static eHalStatus sme_QosUpdateParams(v_U8_t sessionId, |
| sme_QosEdcaAcType ac, |
| v_U8_t tspec_mask, |
| sme_QosWmmTspecInfo * pTspec_Info) |
| { |
| tListElem *pEntry= NULL, *pNextEntry = NULL; |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| sme_QosWmmTspecInfo Tspec_Info; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: invoked on session %d for AC %d TSPEC %d", |
| __func__, __LINE__, |
| sessionId, ac, tspec_mask); |
| if(!pTspec_Info) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: output is NULL, can't aggregate", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| vos_mem_zero(&Tspec_Info, sizeof(sme_QosWmmTspecInfo)); |
| pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE ); |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Flow List empty, nothing to update", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| //init the TS info field |
| Tspec_Info.ts_info.up = pACInfo->curr_QoSInfo[tspec_mask - 1].ts_info.up; |
| Tspec_Info.ts_info.psb = pACInfo->curr_QoSInfo[tspec_mask - 1].ts_info.psb; |
| Tspec_Info.ts_info.tid = pACInfo->curr_QoSInfo[tspec_mask - 1].ts_info.tid; |
| while( pEntry ) |
| { |
| pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE ); |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| if((sessionId == flow_info->sessionId) && |
| (ac == flow_info->ac_type) && |
| (tspec_mask == flow_info->tspec_mask)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Flow %d matches", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| |
| if((SME_QOS_REASON_RELEASE == flow_info->reason ) || |
| (SME_QOS_REASON_MODIFY == flow_info->reason)) |
| { |
| //msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Skipping Flow %d as it is marked " |
| "for release/modify", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| } |
| else if(!HAL_STATUS_SUCCESS(sme_QosAggregateParams(&flow_info->QoSInfo, |
| &Tspec_Info, |
| NULL))) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: sme_QosAggregateParams() failed", |
| __func__, __LINE__); |
| } |
| } |
| pEntry = pNextEntry; |
| } |
| // return the aggregate |
| *pTspec_Info = Tspec_Info; |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosAcToUp() - Utility function to map an AC to UP |
| Note: there is a quantization loss here because 4 ACs are mapped to 8 UPs |
| Mapping is done for consistency |
| \param ac - Enumeration of the various EDCA Access Categories. |
| \return an User Priority |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| sme_QosWmmUpType sme_QosAcToUp(sme_QosEdcaAcType ac) |
| { |
| sme_QosWmmUpType up = SME_QOS_WMM_UP_MAX; |
| if(ac >= 0 && ac < SME_QOS_EDCA_AC_MAX) |
| { |
| up = sme_QosACtoUPMap[ac]; |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: ac = %d up = %d returned", |
| __func__, __LINE__, ac, up); |
| return up; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosUpToAc() - Utility function to map an UP to AC |
| \param up - Enumeration of the various User priorities (UP). |
| \return an Access Category |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| sme_QosEdcaAcType sme_QosUpToAc(sme_QosWmmUpType up) |
| { |
| sme_QosEdcaAcType ac = SME_QOS_EDCA_AC_MAX; |
| if(up >= 0 && up < SME_QOS_WMM_UP_MAX) |
| { |
| ac = sme_QosUPtoACMap[up]; |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: up = %d ac = %d returned", |
| __func__, __LINE__, up, ac); |
| return ac; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosStateTransition() - The state transition function per AC. We |
| save the previous state also. |
| \param sessionId - Session upon which the state machine is running |
| \param ac - Enumeration of the various EDCA Access Categories. |
| \param new_state - The state FSM is moving to. |
| |
| \return None |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| static void sme_QosStateTransition(v_U8_t sessionId, |
| sme_QosEdcaAcType ac, |
| sme_QosStates new_state) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| pACInfo->prev_state = pACInfo->curr_state; |
| pACInfo->curr_state = new_state; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d new state=%d, old state=%d, for AC=%d", |
| __func__, __LINE__, |
| sessionId, pACInfo->curr_state, pACInfo->prev_state, ac ); |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosFindInFlowList() - Utility function to find an flow entry from |
| the flow_list. |
| \param search_key - We can either use the flowID or the ac type to find the |
| entry in the flow list. |
| A bitmap in sme_QosSearchInfo tells which key to use. Starting from LSB, |
| bit 0 - Flow ID |
| bit 1 - AC type |
| \return the pointer to the entry in the link list |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| tListElem *sme_QosFindInFlowList(sme_QosSearchInfo search_key) |
| { |
| tListElem *pEntry= NULL, *pNextEntry = NULL; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE ); |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Flow List empty, can't search", |
| __func__, __LINE__); |
| return NULL; |
| } |
| while( pEntry ) |
| { |
| pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE ); |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| if((search_key.sessionId == flow_info->sessionId) || |
| (search_key.sessionId == SME_QOS_SEARCH_SESSION_ID_ANY)) |
| { |
| if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_1) |
| { |
| if(search_key.key.QosFlowID == flow_info->QosFlowID) |
| { |
| //msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: match found on flowID, ending search", |
| __func__, __LINE__); |
| break; |
| } |
| } |
| else if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_2) |
| { |
| if(search_key.key.ac_type == flow_info->ac_type) |
| { |
| //msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: match found on ac, ending search", |
| __func__, __LINE__); |
| break; |
| } |
| } |
| else if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_3) |
| { |
| if(search_key.key.reason == flow_info->reason) |
| { |
| //msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: match found on reason, ending search", |
| __func__, __LINE__); |
| break; |
| } |
| } |
| else if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_4) |
| { |
| if((search_key.key.ac_type == flow_info->ac_type) && |
| (search_key.direction == flow_info->QoSInfo.ts_info.direction)) |
| { |
| //msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: match found on reason, ending search", |
| __func__, __LINE__); |
| |
| break; |
| } |
| } |
| } |
| pEntry = pNextEntry; |
| } |
| return pEntry; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosFindAllInFlowList() - Utility function to find an flow entry |
| from the flow_list & act on it. |
| \param search_key - We can either use the flowID or the ac type to find the |
| entry in the flow list. |
| A bitmap in sme_QosSearchInfo tells which key to use. Starting from LSB, |
| bit 0 - Flow ID |
| bit 1 - AC type |
| \param fnp - function pointer specifying the action type for the entry found |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosFindAllInFlowList(tpAniSirGlobal pMac, |
| sme_QosSearchInfo search_key, |
| sme_QosProcessSearchEntry fnp) |
| { |
| tListElem *pEntry= NULL, *pNextEntry = NULL; |
| sme_QosSessionInfo *pSession; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE ); |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Flow List empty, can't search", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| while( pEntry ) |
| { |
| pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE ); |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| pSession = &sme_QosCb.sessionInfo[flow_info->sessionId]; |
| if((search_key.sessionId == flow_info->sessionId) || |
| (search_key.sessionId == SME_QOS_SEARCH_SESSION_ID_ANY)) |
| { |
| if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_1) |
| { |
| if(search_key.key.QosFlowID == flow_info->QosFlowID) |
| { |
| //msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: match found on flowID, ending search", |
| __func__, __LINE__); |
| status = fnp(pMac, pEntry); |
| if(eHAL_STATUS_FAILURE == status) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Failed to process entry", |
| __func__, __LINE__); |
| break; |
| } |
| } |
| } |
| else if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_2) |
| { |
| if(search_key.key.ac_type == flow_info->ac_type) |
| { |
| //msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: match found on ac, ending search", |
| __func__, __LINE__); |
| flow_info->hoRenewal = pSession->ac_info[flow_info->ac_type].hoRenewal; |
| status = fnp(pMac, pEntry); |
| if(eHAL_STATUS_FAILURE == status) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Failed to process entry", |
| __func__, __LINE__); |
| break; |
| } |
| } |
| } |
| } |
| pEntry = pNextEntry; |
| } |
| return status; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosIsACM() - Utility function to check if a particular AC |
| mandates Admission Control. |
| \param ac - Enumeration of the various EDCA Access Categories. |
| |
| \return VOS_TRUE if the AC mandates Admission Control |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| v_BOOL_t sme_QosIsACM(tpAniSirGlobal pMac, tSirBssDescription *pSirBssDesc, |
| sme_QosEdcaAcType ac, tDot11fBeaconIEs *pIes) |
| { |
| v_BOOL_t ret_val = VOS_FALSE; |
| tDot11fBeaconIEs *pIesLocal; |
| if(!pSirBssDesc) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: pSirBssDesc is NULL", |
| __func__, __LINE__); |
| return VOS_FALSE; |
| } |
| |
| if (NULL != pIes) |
| { |
| /* IEs were provided so use them locally */ |
| pIesLocal = pIes; |
| } |
| else |
| { |
| /* IEs were not provided so parse them ourselves */ |
| if (!HAL_STATUS_SUCCESS(csrGetParsedBssDescriptionIEs(pMac, pSirBssDesc, &pIesLocal))) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: csrGetParsedBssDescriptionIEs() failed", |
| __func__, __LINE__); |
| return VOS_FALSE; |
| } |
| |
| /* if success then pIesLocal was allocated */ |
| } |
| |
| if(CSR_IS_QOS_BSS(pIesLocal)) |
| { |
| switch(ac) |
| { |
| case SME_QOS_EDCA_AC_BE: |
| if(pIesLocal->WMMParams.acbe_acm) ret_val = VOS_TRUE; |
| break; |
| case SME_QOS_EDCA_AC_BK: |
| if(pIesLocal->WMMParams.acbk_acm) ret_val = VOS_TRUE; |
| break; |
| case SME_QOS_EDCA_AC_VI: |
| if(pIesLocal->WMMParams.acvi_acm) ret_val = VOS_TRUE; |
| break; |
| case SME_QOS_EDCA_AC_VO: |
| if(pIesLocal->WMMParams.acvo_acm) ret_val = VOS_TRUE; |
| break; |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: unknown AC = %d", |
| __func__, __LINE__, ac); |
| //Assert |
| VOS_ASSERT(0); |
| break; |
| } |
| }//IS_QOS_BSS |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: ACM = %d for AC = %d", |
| __func__, __LINE__, ret_val, ac ); |
| if (NULL == pIes) |
| { |
| /* IEs were allocated locally so free them */ |
| vos_mem_free(pIesLocal); |
| } |
| return ret_val; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosBufferExistingFlows() - Utility function to buffer the existing |
| flows in flow_list, so that we can renew them after handoff is done. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| static eHalStatus sme_QosBufferExistingFlows(tpAniSirGlobal pMac, |
| v_U8_t sessionId) |
| { |
| tListElem *pEntry= NULL, *pNextEntry = NULL; |
| sme_QosSessionInfo *pSession; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| sme_QosCmdInfo cmd; |
| pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE ); |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Flow List empty, nothing to buffer", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| while( pEntry ) |
| { |
| pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE ); |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| if (flow_info->sessionId == sessionId) |
| { |
| if((SME_QOS_REASON_REQ_SUCCESS == flow_info->reason )|| |
| (SME_QOS_REASON_SETUP == flow_info->reason )) |
| { |
| cmd.command = SME_QOS_SETUP_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.setupCmdInfo.HDDcontext = flow_info->HDDcontext; |
| cmd.u.setupCmdInfo.QoSInfo = flow_info->QoSInfo; |
| cmd.u.setupCmdInfo.QoSCallback = flow_info->QoSCallback; |
| cmd.u.setupCmdInfo.UPType = SME_QOS_WMM_UP_MAX;//shouldn't be needed |
| cmd.u.setupCmdInfo.QosFlowID = flow_info->QosFlowID; |
| if(SME_QOS_REASON_SETUP == flow_info->reason ) |
| { |
| cmd.u.setupCmdInfo.hoRenewal = VOS_FALSE; |
| } |
| else |
| { |
| cmd.u.setupCmdInfo.hoRenewal = VOS_TRUE;//TODO: might need this for modify |
| } |
| if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_TRUE))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't buffer the setup request for " |
| "flow %d in handoff state", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: buffered a setup request for " |
| "flow %d in handoff state", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| } |
| } |
| else if(SME_QOS_REASON_RELEASE == flow_info->reason ) |
| { |
| cmd.command = SME_QOS_RELEASE_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.releaseCmdInfo.QosFlowID = flow_info->QosFlowID; |
| if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_TRUE))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't buffer the release request for " |
| "flow %d in handoff state", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: buffered a release request for " |
| "flow %d in handoff state", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| } |
| } |
| else if(SME_QOS_REASON_MODIFY_PENDING == flow_info->reason) |
| { |
| cmd.command = SME_QOS_MODIFY_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.modifyCmdInfo.QosFlowID = flow_info->QosFlowID; |
| cmd.u.modifyCmdInfo.QoSInfo = flow_info->QoSInfo; |
| if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_TRUE))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't buffer the modify request for " |
| "flow %d in handoff state", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: buffered a modify request for " |
| "flow %d in handoff state", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| } |
| } |
| //delete the entry from Flow List |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Deleting original entry at %pK with flowID %d", |
| __func__, __LINE__, |
| flow_info, flow_info->QosFlowID); |
| csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE ); |
| vos_mem_free(flow_info); |
| } |
| pEntry = pNextEntry; |
| } |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| pSession->uapsdAlreadyRequested = VOS_FALSE; |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosDeleteExistingFlows() - Utility function to Delete the existing |
| flows in flow_list, if we lost connectivity. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| static eHalStatus sme_QosDeleteExistingFlows(tpAniSirGlobal pMac, |
| v_U8_t sessionId) |
| { |
| tListElem *pEntry= NULL, *pNextEntry = NULL; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_TRUE ); |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN, |
| "%s: %d: Flow List empty, nothing to delete", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| while( pEntry ) |
| { |
| pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_TRUE ); |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| if (flow_info->sessionId == sessionId) |
| { |
| if((SME_QOS_REASON_REQ_SUCCESS == flow_info->reason )|| |
| (SME_QOS_REASON_SETUP == flow_info->reason )|| |
| (SME_QOS_REASON_RELEASE == flow_info->reason )|| |
| (SME_QOS_REASON_MODIFY == flow_info->reason )) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| NULL, |
| SME_QOS_STATUS_RELEASE_QOS_LOST_IND, |
| flow_info->QosFlowID); |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Deleting entry at %pK with flowID %d", |
| __func__, __LINE__, |
| flow_info, flow_info->QosFlowID); |
| //delete the entry from Flow List |
| csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE ); |
| vos_mem_free(flow_info); |
| } |
| pEntry = pNextEntry; |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosBufferCmd() - Utility function to buffer a request (setup/modify/ |
| release) from client while processing another one on the same AC. |
| \param pcmd - a pointer to the cmd structure to be saved inside the buffered |
| cmd link list |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosBufferCmd(sme_QosCmdInfo *pcmd, v_BOOL_t insert_head) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosCmdInfoEntry * pentry = NULL; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Invoked", |
| __func__, __LINE__); |
| pentry = (sme_QosCmdInfoEntry *) vos_mem_malloc(sizeof(sme_QosCmdInfoEntry)); |
| if (!pentry) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Memory allocation failure", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| // copy the entire CmdInfo |
| pentry->cmdInfo = *pcmd; |
| |
| pSession = &sme_QosCb.sessionInfo[pcmd->sessionId]; |
| if(insert_head) |
| { |
| csrLLInsertHead(&pSession->bufferedCommandList, &pentry->link, VOS_TRUE); |
| } |
| else |
| { |
| csrLLInsertTail(&pSession->bufferedCommandList, &pentry->link, VOS_TRUE); |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessBufferedCmd() - Utility function to process a buffered |
| request (setup/modify/release) initially came from the client. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| static eHalStatus sme_QosProcessBufferedCmd(v_U8_t sessionId) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosCmdInfoEntry *pcmd = NULL; |
| tListElem *pEntry= NULL; |
| sme_QosStatusType hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| eHalStatus halStatus = eHAL_STATUS_SUCCESS; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Invoked on session %d", |
| __func__, __LINE__, |
| sessionId); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| if(!csrLLIsListEmpty( &pSession->bufferedCommandList, VOS_FALSE )) |
| { |
| pEntry = csrLLRemoveHead( &pSession->bufferedCommandList, VOS_TRUE ); |
| if(!pEntry) |
| { |
| //Err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: no more buffered commands on session %d", |
| __func__, __LINE__, |
| sessionId); |
| pSession->readyForPowerSave = VOS_TRUE; |
| return eHAL_STATUS_FAILURE; |
| } |
| pcmd = GET_BASE_ADDR( pEntry, sme_QosCmdInfoEntry, link ); |
| switch(pcmd->cmdInfo.command) |
| { |
| case SME_QOS_SETUP_REQ: |
| hdd_status = sme_QosInternalSetupReq(pcmd->cmdInfo.pMac, |
| pcmd->cmdInfo.sessionId, |
| &pcmd->cmdInfo.u.setupCmdInfo.QoSInfo, |
| pcmd->cmdInfo.u.setupCmdInfo.QoSCallback, |
| pcmd->cmdInfo.u.setupCmdInfo.HDDcontext, |
| pcmd->cmdInfo.u.setupCmdInfo.UPType, |
| pcmd->cmdInfo.u.setupCmdInfo.QosFlowID, |
| VOS_TRUE, |
| pcmd->cmdInfo.u.setupCmdInfo.hoRenewal); |
| if(SME_QOS_STATUS_SETUP_FAILURE_RSP == hdd_status) |
| { |
| //Err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: sme_QosInternalSetupReq failed on session %d", |
| __func__, __LINE__, |
| sessionId); |
| halStatus = eHAL_STATUS_FAILURE; |
| } |
| break; |
| case SME_QOS_RELEASE_REQ: |
| hdd_status = sme_QosInternalReleaseReq(pcmd->cmdInfo.pMac, |
| pcmd->cmdInfo.sessionId, |
| pcmd->cmdInfo.u.releaseCmdInfo.QosFlowID, |
| VOS_TRUE); |
| if(SME_QOS_STATUS_RELEASE_FAILURE_RSP == hdd_status) |
| { |
| //Err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: sme_QosInternalReleaseReq failed on session %d", |
| __func__, __LINE__, |
| sessionId); |
| halStatus = eHAL_STATUS_FAILURE; |
| } |
| break; |
| case SME_QOS_MODIFY_REQ: |
| hdd_status = sme_QosInternalModifyReq(pcmd->cmdInfo.pMac, |
| &pcmd->cmdInfo.u.modifyCmdInfo.QoSInfo, |
| pcmd->cmdInfo.u.modifyCmdInfo.QosFlowID, |
| VOS_TRUE); |
| if(SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP == hdd_status) |
| { |
| //Err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: sme_QosInternalModifyReq failed on session %d", |
| __func__, __LINE__, |
| sessionId); |
| halStatus = eHAL_STATUS_FAILURE; |
| } |
| break; |
| case SME_QOS_RESEND_REQ: |
| hdd_status = sme_QosReRequestAddTS(pcmd->cmdInfo.pMac, |
| pcmd->cmdInfo.sessionId, |
| &pcmd->cmdInfo.u.resendCmdInfo.QoSInfo, |
| pcmd->cmdInfo.u.resendCmdInfo.ac, |
| pcmd->cmdInfo.u.resendCmdInfo.tspecMask); |
| if(SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP == hdd_status) |
| { |
| //Err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: sme_QosReRequestAddTS failed on session %d", |
| __func__, __LINE__, |
| sessionId); |
| halStatus = eHAL_STATUS_FAILURE; |
| } |
| break; |
| default: |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d unknown cmd = %d", |
| __func__, __LINE__, |
| sessionId, pcmd->cmdInfo.command); |
| VOS_ASSERT(0); |
| break; |
| } |
| // buffered command has been processed, reclaim the memory |
| vos_mem_free(pcmd); |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: cmd buffer empty", |
| __func__, __LINE__); |
| pSession->readyForPowerSave = VOS_TRUE; |
| } |
| return halStatus; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosDeleteBufferedRequests() - Utility function to Delete the buffered |
| requests in the buffered_cmd_list, if we lost connectivity. |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| static eHalStatus sme_QosDeleteBufferedRequests(tpAniSirGlobal pMac, |
| v_U8_t sessionId) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosCmdInfoEntry *pcmd = NULL; |
| tListElem *pEntry= NULL, *pNextEntry = NULL; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Invoked on session %d", |
| __func__, __LINE__, sessionId); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| pEntry = csrLLPeekHead( &pSession->bufferedCommandList, VOS_TRUE ); |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN, |
| "%s: %d: Buffered List empty, nothing to delete on session %d", |
| __func__, __LINE__, |
| sessionId); |
| return eHAL_STATUS_FAILURE; |
| } |
| while( pEntry ) |
| { |
| pNextEntry = csrLLNext( &pSession->bufferedCommandList, pEntry, VOS_TRUE ); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| "%s: %d: deleting entry from buffered List", |
| __func__, __LINE__); |
| //delete the entry from Flow List |
| csrLLRemoveEntry(&pSession->bufferedCommandList, pEntry, VOS_TRUE ); |
| // reclaim the memory |
| pcmd = GET_BASE_ADDR( pEntry, sme_QosCmdInfoEntry, link ); |
| vos_mem_free(pcmd); |
| pEntry = pNextEntry; |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosSaveAssocInfo() - Utility function to save the assoc info in the |
| CB like BSS descriptor of the AP, the profile that HDD sent down with the |
| connect request, while CSR notifies for assoc/reassoc success. |
| \param pAssoc_info - pointer to the assoc structure to store the BSS |
| descriptor of the AP, the profile that HDD sent down with |
| the connect request |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosSaveAssocInfo(sme_QosSessionInfo *pSession, sme_QosAssocInfo *pAssoc_info) |
| { |
| tSirBssDescription *pBssDesc = NULL; |
| v_U32_t bssLen = 0; |
| if(NULL == pAssoc_info) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: pAssoc_info is NULL", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| //clean up the assoc info if already set |
| if(pSession->assocInfo.pBssDesc) |
| { |
| vos_mem_free(pSession->assocInfo.pBssDesc); |
| pSession->assocInfo.pBssDesc = NULL; |
| } |
| bssLen = pAssoc_info->pBssDesc->length + |
| sizeof(pAssoc_info->pBssDesc->length); |
| //save the bss Descriptor |
| pBssDesc = (tSirBssDescription *)vos_mem_malloc(bssLen); |
| if (!pBssDesc) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't allocate memory for the bss Descriptor", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| vos_mem_copy(pBssDesc, pAssoc_info->pBssDesc, bssLen); |
| pSession->assocInfo.pBssDesc = pBssDesc; |
| //save the apsd info from assoc |
| if(pAssoc_info->pProfile) |
| { |
| pSession->apsdMask |= pAssoc_info->pProfile->uapsd_mask; |
| } |
| // [TODO] Do we need to update the global APSD bitmap? |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosSetupFnp() - Utility function (pointer) to notify other entries |
| in FLOW list on the same AC that qos params got modified |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure) |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosSetupFnp(tpAniSirGlobal pMac, tListElem *pEntry) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| sme_QosStatusType hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND; |
| sme_QosEdcaAcType ac; |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Entry is NULL", |
| __func__, __LINE__); |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| ac = flow_info->ac_type; |
| pSession = &sme_QosCb.sessionInfo[flow_info->sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| if(SME_QOS_REASON_REQ_SUCCESS == flow_info->reason) |
| { |
| //notify HDD, only the other Flows running on the AC |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pACInfo->curr_QoSInfo[flow_info->tspec_mask - 1], |
| hdd_status, |
| flow_info->QosFlowID); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Entry with flowID = %d getting notified", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosModificationNotifyFnp() - Utility function (pointer) to notify |
| other entries in FLOW list on the same AC that qos params got modified |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure) |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosModificationNotifyFnp(tpAniSirGlobal pMac, tListElem *pEntry) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| sme_QosStatusType hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND; |
| sme_QosEdcaAcType ac; |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Entry is NULL", |
| __func__, __LINE__); |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| ac = flow_info->ac_type; |
| pSession = &sme_QosCb.sessionInfo[flow_info->sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| if(SME_QOS_REASON_REQ_SUCCESS == flow_info->reason) |
| { |
| //notify HDD, only the other Flows running on the AC |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pACInfo->curr_QoSInfo[flow_info->tspec_mask - 1], |
| hdd_status, |
| flow_info->QosFlowID); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Entry with flowID = %d getting notified", |
| __func__, __LINE__, |
| flow_info->QosFlowID); |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosModifyFnp() - Utility function (pointer) to delete the original |
| entry in FLOW list & add the modified one |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure) |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosModifyFnp(tpAniSirGlobal pMac, tListElem *pEntry) |
| { |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Entry is NULL", |
| __func__, __LINE__); |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| switch(flow_info->reason) |
| { |
| case SME_QOS_REASON_MODIFY_PENDING: |
| //set the proper reason code for the new (with modified params) entry |
| flow_info->reason = SME_QOS_REASON_REQ_SUCCESS; |
| break; |
| case SME_QOS_REASON_MODIFY: |
| //delete the original entry from Flow List |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Deleting original entry at %pK with flowID %d", |
| __func__, __LINE__, |
| flow_info, flow_info->QosFlowID); |
| csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE ); |
| // reclaim the memory |
| vos_mem_free(flow_info); |
| break; |
| default: |
| break; |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosDelTsIndFnp() - Utility function (pointer) to find all Flows on |
| the particular AC & delete them, also send HDD indication through the callback |
| it registered per request |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure) |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosDelTsIndFnp(tpAniSirGlobal pMac, tListElem *pEntry) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| sme_QosEdcaAcType ac; |
| eHalStatus lock_status = eHAL_STATUS_FAILURE; |
| sme_QosStatusType status; |
| |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Entry is NULL", |
| __func__, __LINE__); |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| //delete the entry from Flow List |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| ac = flow_info->ac_type; |
| pSession = &sme_QosCb.sessionInfo[flow_info->sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| pACInfo->relTrig = SME_QOS_RELEASE_BY_AP; |
| |
| lock_status = sme_AcquireGlobalLock( &pMac->sme ); |
| if ( !HAL_STATUS_SUCCESS( lock_status ) ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Unable to obtain lock", |
| __func__, __LINE__); |
| return SME_QOS_STATUS_RELEASE_FAILURE_RSP; |
| } |
| //Call the internal function for QoS release, adding a layer of abstraction |
| status = sme_QosInternalReleaseReq(pMac, flow_info->sessionId, |
| flow_info->QosFlowID, VOS_FALSE); |
| sme_ReleaseGlobalLock( &pMac->sme ); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: QoS Release return status on Flow %d is %d", |
| __func__, __LINE__, |
| flow_info->QosFlowID, status); |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosReassocSuccessEvFnp() - Utility function (pointer) to notify HDD |
| the success for the requested flow & notify all the other flows running on the |
| same AC that QoS params got modified |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure) |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosReassocSuccessEvFnp(tpAniSirGlobal pMac, tListElem *pEntry) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| v_BOOL_t delete_entry = VOS_FALSE; |
| sme_QosStatusType hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| sme_QosEdcaAcType ac; |
| eHalStatus pmc_status = eHAL_STATUS_FAILURE; |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Entry is NULL", |
| __func__, __LINE__); |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| flow_info = GET_BASE_ADDR(pEntry, sme_QosFlowInfoEntry, link); |
| ac = flow_info->ac_type; |
| pSession = &sme_QosCb.sessionInfo[flow_info->sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| switch(flow_info->reason) |
| { |
| case SME_QOS_REASON_SETUP: |
| hdd_status = SME_QOS_STATUS_SETUP_SUCCESS_IND; |
| delete_entry = VOS_FALSE; |
| flow_info->reason = SME_QOS_REASON_REQ_SUCCESS; |
| //check for the case where we had to do reassoc to reset the apsd bit |
| //for the ac - release or modify scenario |
| if(pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0].ts_info.psb) |
| { |
| // notify PMC as App is looking for APSD. If we already requested |
| // then we don't need to do anything. |
| if(!pSession->uapsdAlreadyRequested) |
| { |
| // this is the first flow to detect we need PMC in UAPSD mode |
| if(!pMac->psOffloadEnabled) |
| { |
| pmc_status = pmcStartUapsd(pMac, |
| sme_QosPmcStartUapsdCallback, |
| pSession); |
| } |
| else |
| { |
| pmc_status = pmcOffloadStartUapsd(pMac, |
| flow_info->sessionId, |
| sme_QosPmcOffloadStartUapsdCallback, |
| pSession); |
| } |
| |
| // if PMC doesn't return success right away means it is yet to put |
| // the module in BMPS state & later to UAPSD state |
| |
| if(eHAL_STATUS_FAILURE == pmc_status) |
| { |
| hdd_status = SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_SET_FAILED; |
| //we need to always notify this case |
| flow_info->hoRenewal = VOS_FALSE; |
| } |
| else if(eHAL_STATUS_PMC_PENDING == pmc_status) |
| { |
| // let other flows know PMC has been notified |
| pSession->uapsdAlreadyRequested = VOS_TRUE; |
| } |
| // for any other pmc status we declare success |
| } |
| } |
| break; |
| case SME_QOS_REASON_RELEASE: |
| pACInfo->num_flows[SME_QOS_TSPEC_INDEX_0]--; |
| // fall through |
| case SME_QOS_REASON_MODIFY: |
| delete_entry = VOS_TRUE; |
| break; |
| case SME_QOS_REASON_MODIFY_PENDING: |
| hdd_status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND; |
| delete_entry = VOS_FALSE; |
| flow_info->reason = SME_QOS_REASON_REQ_SUCCESS; |
| if(pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0].ts_info.psb) |
| { |
| |
| if(!pSession->uapsdAlreadyRequested) |
| { |
| if(!pMac->psOffloadEnabled) |
| { |
| // this is the first flow to detect we need PMC in UAPSD mode |
| pmc_status = pmcStartUapsd(pMac, |
| sme_QosPmcStartUapsdCallback, |
| pSession); |
| } |
| else |
| { |
| // this is the first flow to detect we need PMC in UAPSD mode |
| pmc_status = pmcOffloadStartUapsd(pMac, |
| flow_info->sessionId, |
| sme_QosPmcOffloadStartUapsdCallback, |
| pSession); |
| } |
| |
| // if PMC doesn't return success right away means it is yet to put |
| // the module in BMPS state & later to UAPSD state |
| if(eHAL_STATUS_FAILURE == pmc_status) |
| { |
| hdd_status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_SET_FAILED; |
| // we need to always notify this case |
| flow_info->hoRenewal = VOS_FALSE; |
| } |
| else if(eHAL_STATUS_PMC_PENDING == pmc_status) |
| { |
| pSession->uapsdAlreadyRequested = VOS_TRUE; |
| } |
| // for any other pmc status we declare success |
| } |
| } |
| break; |
| case SME_QOS_REASON_REQ_SUCCESS: |
| hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND; |
| // fall through |
| default: |
| delete_entry = VOS_FALSE; |
| break; |
| } |
| if(!delete_entry) |
| { |
| if(!flow_info->hoRenewal) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0], |
| hdd_status, |
| flow_info->QosFlowID); |
| } |
| else |
| { |
| flow_info->hoRenewal = VOS_FALSE; |
| } |
| } |
| else |
| { |
| //delete the entry from Flow List |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Deleting entry at %pK with flowID %d", |
| __func__, __LINE__, |
| flow_info, flow_info->QosFlowID); |
| csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE ); |
| // reclaim the memory |
| vos_mem_free(flow_info); |
| } |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosAddTsFailureFnp() - Utility function (pointer), |
| if the Addts request was for for an flow setup request, delete the entry from |
| Flow list & notify HDD |
| if the Addts request was for downgrading of QoS params because of an flow |
| release requested on the AC, delete the entry from Flow list & notify HDD |
| if the Addts request was for change of QoS params because of an flow |
| modification requested on the AC, delete the new entry from Flow list & notify |
| HDD |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure) |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosAddTsFailureFnp(tpAniSirGlobal pMac, tListElem *pEntry) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| v_BOOL_t inform_hdd = VOS_FALSE; |
| sme_QosStatusType hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| sme_QosEdcaAcType ac; |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Entry is NULL", |
| __func__, __LINE__); |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| ac = flow_info->ac_type; |
| pSession = &sme_QosCb.sessionInfo[flow_info->sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| switch(flow_info->reason) |
| { |
| case SME_QOS_REASON_SETUP: |
| hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| pACInfo->num_flows[pACInfo->tspec_pending - 1]--; |
| inform_hdd = VOS_TRUE; |
| break; |
| case SME_QOS_REASON_RELEASE: |
| hdd_status = SME_QOS_STATUS_RELEASE_FAILURE_RSP; |
| pACInfo->num_flows[pACInfo->tspec_pending - 1]--; |
| inform_hdd = VOS_TRUE; |
| break; |
| case SME_QOS_REASON_MODIFY_PENDING: |
| hdd_status = SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| inform_hdd = VOS_TRUE; |
| break; |
| case SME_QOS_REASON_MODIFY: |
| flow_info->reason = SME_QOS_REASON_REQ_SUCCESS; |
| case SME_QOS_REASON_REQ_SUCCESS: |
| default: |
| inform_hdd = VOS_FALSE; |
| break; |
| } |
| if(inform_hdd) |
| { |
| //notify HDD, only the requested Flow, other Flows running on the AC stay |
| // intact |
| if(!flow_info->hoRenewal) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pACInfo->curr_QoSInfo[pACInfo->tspec_pending - 1], |
| hdd_status, |
| flow_info->QosFlowID); |
| } |
| else |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pACInfo->curr_QoSInfo[pACInfo->tspec_pending - 1], |
| SME_QOS_STATUS_RELEASE_QOS_LOST_IND, |
| flow_info->QosFlowID); |
| } |
| //delete the entry from Flow List |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Deleting entry at %pK with flowID %d", |
| __func__, __LINE__, |
| flow_info, flow_info->QosFlowID); |
| csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE ); |
| // reclaim the memory |
| vos_mem_free(flow_info); |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosAddTsSuccessFnp() - Utility function (pointer), |
| if the Addts request was for for an flow setup request, notify HDD for success |
| for the flow & notify all the other flows running on the same AC that QoS |
| params got modified |
| if the Addts request was for downgrading of QoS params because of an flow |
| release requested on the AC, delete the entry from Flow list & notify HDD |
| if the Addts request was for change of QoS params because of an flow |
| modification requested on the AC, delete the old entry from Flow list & notify |
| HDD for success for the flow & notify all the other flows running on the same |
| AC that QoS params got modified |
| \param pMac - Pointer to the global MAC parameter structure. |
| \param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure) |
| |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosAddTsSuccessFnp(tpAniSirGlobal pMac, tListElem *pEntry) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| v_BOOL_t inform_hdd = VOS_FALSE; |
| v_BOOL_t delete_entry = VOS_FALSE; |
| sme_QosStatusType hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP; |
| sme_QosEdcaAcType ac; |
| eHalStatus pmc_status = eHAL_STATUS_FAILURE; |
| tCsrRoamModifyProfileFields modifyProfileFields; |
| |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Entry is NULL", |
| __func__, __LINE__); |
| VOS_ASSERT(0); |
| return eHAL_STATUS_FAILURE; |
| } |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| ac = flow_info->ac_type; |
| pSession = &sme_QosCb.sessionInfo[flow_info->sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| if(flow_info->tspec_mask != pACInfo->tspec_pending) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: No need to notify the HDD, the ADDTS " |
| "success is not for index = %d of the AC = %d", |
| __func__, __LINE__, |
| flow_info->tspec_mask, ac); |
| return eHAL_STATUS_SUCCESS; |
| } |
| switch(flow_info->reason) |
| { |
| case SME_QOS_REASON_SETUP: |
| hdd_status = SME_QOS_STATUS_SETUP_SUCCESS_IND; |
| flow_info->reason = SME_QOS_REASON_REQ_SUCCESS; |
| delete_entry = VOS_FALSE; |
| inform_hdd = VOS_TRUE; |
| // check if App is looking for APSD |
| if(pACInfo->requested_QoSInfo[pACInfo->tspec_pending - 1].ts_info.psb) |
| { |
| // notify PMC as App is looking for APSD. If we already requested |
| // then we don't need to do anything |
| if(!pSession->uapsdAlreadyRequested) |
| { |
| if(!pMac->psOffloadEnabled) |
| { |
| // this is the first flow to detect we need PMC in UAPSD mode |
| pmc_status = pmcStartUapsd(pMac, |
| sme_QosPmcStartUapsdCallback, |
| pSession); |
| } |
| else |
| { |
| // this is the first flow to detect we need PMC in UAPSD mode |
| pmc_status = pmcOffloadStartUapsd(pMac, |
| flow_info->sessionId, |
| sme_QosPmcOffloadStartUapsdCallback, |
| pSession); |
| } |
| |
| // if PMC doesn't return success right away means it is yet to put |
| // the module in BMPS state & later to UAPSD state |
| if(eHAL_STATUS_FAILURE == pmc_status) |
| { |
| hdd_status = SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_SET_FAILED; |
| // we need to always notify this case |
| flow_info->hoRenewal = VOS_FALSE; |
| } |
| else if(eHAL_STATUS_PMC_PENDING == pmc_status) |
| { |
| // let other flows know PMC has been notified |
| pSession->uapsdAlreadyRequested = VOS_TRUE; |
| } |
| // for any other pmc status we declare success |
| } |
| } |
| break; |
| case SME_QOS_REASON_RELEASE: |
| pACInfo->num_flows[pACInfo->tspec_pending - 1]--; |
| hdd_status = SME_QOS_STATUS_RELEASE_SUCCESS_RSP; |
| inform_hdd = VOS_TRUE; |
| delete_entry = VOS_TRUE; |
| break; |
| case SME_QOS_REASON_MODIFY: |
| delete_entry = VOS_TRUE; |
| inform_hdd = VOS_FALSE; |
| break; |
| case SME_QOS_REASON_MODIFY_PENDING: |
| hdd_status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND; |
| delete_entry = VOS_FALSE; |
| flow_info->reason = SME_QOS_REASON_REQ_SUCCESS; |
| inform_hdd = VOS_TRUE; |
| //notify PMC if App is looking for APSD |
| if(pACInfo->requested_QoSInfo[pACInfo->tspec_pending - 1].ts_info.psb) |
| { |
| // notify PMC as App is looking for APSD. If we already requested |
| // then we don't need to do anything. |
| if(!pSession->uapsdAlreadyRequested) |
| { |
| if(!pMac->psOffloadEnabled) |
| { |
| // this is the first flow to detect we need PMC in UAPSD mode |
| pmc_status = pmcStartUapsd(pMac, |
| sme_QosPmcStartUapsdCallback, |
| pSession); |
| } |
| else |
| { |
| // this is the first flow to detect we need PMC in UAPSD mode |
| pmc_status = pmcOffloadStartUapsd(pMac, |
| flow_info->sessionId, |
| sme_QosPmcOffloadStartUapsdCallback, |
| pSession); |
| } |
| |
| // if PMC doesn't return success right away means it is yet to put |
| // the module in BMPS state & later to UAPSD state |
| if(eHAL_STATUS_FAILURE == pmc_status) |
| { |
| hdd_status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_SET_FAILED; |
| // we need to always notify this case |
| flow_info->hoRenewal = VOS_FALSE; |
| } |
| else if(eHAL_STATUS_PMC_PENDING == pmc_status) |
| { |
| // let other flows know PMC has been notified |
| pSession->uapsdAlreadyRequested = VOS_TRUE; |
| } |
| // for any other pmc status we declare success |
| } |
| } |
| else |
| { |
| if((pACInfo->num_flows[flow_info->tspec_mask - 1] == 1) && |
| (SME_QOS_TSPEC_MASK_BIT_1_2_SET != pACInfo->tspec_mask_status)) |
| { |
| // this is the only TSPEC active on this AC |
| // so indicate that we no longer require APSD |
| pSession->apsdMask &= ~(1 << (SME_QOS_EDCA_AC_VO - ac)); |
| //Also update modifyProfileFields.uapsd_mask in CSR for consistency |
| csrGetModifyProfileFields(pMac, flow_info->sessionId, &modifyProfileFields); |
| modifyProfileFields.uapsd_mask = pSession->apsdMask; |
| csrSetModifyProfileFields(pMac, flow_info->sessionId, &modifyProfileFields); |
| if(!pSession->apsdMask) |
| { |
| if(!pMac->psOffloadEnabled) |
| { |
| // this session no longer needs UAPSD |
| // do any sessions still require UAPSD? |
| if (!sme_QosIsUapsdActive()) |
| { |
| // No sessions require UAPSD so turn it off |
| // (really don't care when PMC stops it) |
| (void)pmcStopUapsd(pMac); |
| } |
| } |
| else |
| { |
| (void)pmcOffloadStopUapsd(pMac, flow_info->sessionId); |
| } |
| } |
| } |
| } |
| break; |
| case SME_QOS_REASON_REQ_SUCCESS: |
| hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND; |
| inform_hdd = VOS_TRUE; |
| default: |
| delete_entry = VOS_FALSE; |
| break; |
| } |
| if(inform_hdd) |
| { |
| if(!flow_info->hoRenewal) |
| { |
| |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pACInfo->curr_QoSInfo[pACInfo->tspec_pending - 1], |
| hdd_status, |
| flow_info->QosFlowID); |
| } |
| else |
| { |
| /* |
| * For downgrading purpose, Hdd set WmmTspecValid to false during |
| * roaming. Need to set that flag. Call the hdd callback in successful |
| * case. |
| */ |
| if ((hdd_status == SME_QOS_STATUS_SETUP_SUCCESS_IND) |
| #if defined (WLAN_FEATURE_VOWIFI_11R) |
| && |
| (!csrRoamIs11rAssoc(pMac, flow_info->sessionId)) |
| #endif |
| #if defined(FEATURE_WLAN_ESE) |
| && |
| (!csrRoamIsESEAssoc(pMac, flow_info->sessionId)) |
| #endif |
| ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s calling hdd_wmm_smecallback during roaming for ac = %d", __func__, ac); |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pACInfo->curr_QoSInfo[pACInfo->tspec_pending - 1], |
| hdd_status, |
| flow_info->QosFlowID |
| ); |
| } |
| flow_info->hoRenewal = VOS_FALSE; |
| } |
| } |
| if(delete_entry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Deleting entry at %pK with flowID %d", |
| __func__, __LINE__, |
| flow_info, flow_info->QosFlowID); |
| //delete the entry from Flow List |
| csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE ); |
| // reclaim the memory |
| vos_mem_free(flow_info); |
| } |
| |
| return eHAL_STATUS_SUCCESS; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosIsRspPending() - Utility function to check if we are waiting |
| for an AddTS or reassoc response on some AC other than the given AC |
| |
| \param sessionId - Session we are interested in |
| \param ac - Enumeration of the various EDCA Access Categories. |
| |
| \return boolean |
| TRUE - Response is pending on an AC |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| static v_BOOL_t sme_QosIsRspPending(v_U8_t sessionId, sme_QosEdcaAcType ac) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosEdcaAcType acIndex; |
| v_BOOL_t status = VOS_FALSE; |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| for(acIndex = SME_QOS_EDCA_AC_BE; acIndex < SME_QOS_EDCA_AC_MAX; acIndex++) |
| { |
| if(acIndex == ac) |
| { |
| continue; |
| } |
| pACInfo = &pSession->ac_info[acIndex]; |
| if((pACInfo->tspec_pending) || (pACInfo->reassoc_pending)) |
| { |
| status = VOS_TRUE; |
| break; |
| } |
| } |
| return status; |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosUpdateHandOff() - Function which can be called to update |
| Hand-off state of SME QoS Session |
| \param sessionId - session id |
| \param updateHandOff - value True/False to update the handoff flag |
| |
| \sa |
| |
| -------------------------------------------------------------------------*/ |
| void sme_QosUpdateHandOff(v_U8_t sessionId, |
| v_BOOL_t updateHandOff) |
| { |
| sme_QosSessionInfo *pSession; |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED, |
| "%s: %d: handoffRequested %d updateHandOff %d", |
| __func__, __LINE__,pSession->handoffRequested, |
| updateHandOff); |
| |
| pSession->handoffRequested = updateHandOff; |
| |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosIsUapsdActive() - Function which can be called to determine |
| if any sessions require PMC to be in U-APSD mode. |
| \return boolean |
| |
| Returns true if at least one session required PMC to be in U-APSD mode |
| Returns false if no sessions require PMC to be in U-APSD mode |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| static v_BOOL_t sme_QosIsUapsdActive(void) |
| { |
| sme_QosSessionInfo *pSession; |
| v_U8_t sessionId; |
| for (sessionId = 0; sessionId < CSR_ROAM_SESSION_MAX; ++sessionId) |
| { |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| if ((pSession->sessionActive) && (pSession->apsdMask)) |
| { |
| return VOS_TRUE; |
| } |
| } |
| // no active sessions have U-APSD active |
| return VOS_FALSE; |
| } |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosPmcFullPowerCallback() - Callback function registered with PMC |
| to notify SME-QoS when it puts the chip into full power |
| |
| \param callbackContext - The context passed to PMC during pmcRequestFullPower |
| call. |
| \param status - eHalStatus returned by PMC. |
| |
| \return None |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| void sme_QosPmcFullPowerCallback(void *callbackContext, eHalStatus status) |
| { |
| sme_QosSessionInfo *pSession = callbackContext; |
| if(HAL_STATUS_SUCCESS(status)) |
| { |
| (void)sme_QosProcessBufferedCmd(pSession->sessionId); |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: PMC failed to put the chip in Full power", |
| __func__, __LINE__); |
| VOS_ASSERT(0); |
| } |
| } |
| |
| void sme_QosPmcOffloadFullPowerCallback(void *callbackContext, tANI_U32 sessionId, |
| eHalStatus status) |
| { |
| sme_QosSessionInfo *pSession = callbackContext; |
| if(HAL_STATUS_SUCCESS(status)) |
| { |
| (void)sme_QosProcessBufferedCmd(pSession->sessionId); |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: PMC failed to put the chip in Full power", |
| __func__, __LINE__); |
| VOS_ASSERT(0); |
| } |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosPmcStartUAPSDCallback() - Callback function registered with PMC |
| to notify SME-QoS when it puts the chip into UAPSD mode |
| |
| \param callbackContext - The context passed to PMC during pmcStartUapsd call. |
| \param status - eHalStatus returned by PMC. |
| |
| \return None |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| void sme_QosPmcStartUapsdCallback(void *callbackContext, eHalStatus status) |
| { |
| sme_QosSessionInfo *pSession = callbackContext; |
| // NOTE WELL |
| // |
| // In the original QoS design the TL module was responsible for |
| // the generation of trigger frames. When that design was in |
| // use, we had to queue up any flows which were waiting for PMC |
| // since we didn't want to notify HDD until PMC had changed to |
| // UAPSD state. Otherwise HDD would provide TL with the trigger |
| // frame parameters, and TL would start trigger frame generation |
| // before PMC was ready. The flows were queued in various places |
| // throughout this module, and they were dequeued here following |
| // a successful transition to the UAPSD state by PMC. |
| // |
| // In the current QoS design the Firmware is responsible for the |
| // generation of trigger frames, but the parameters are still |
| // provided by TL via HDD. The Firmware will be notified of the |
| // change to UAPSD state directly by PMC, at which time it will be |
| // responsible for the generation of trigger frames. Therefore |
| // where we used to queue up flows waiting for PMC to transition |
| // to the UAPSD state, we now always transition directly to the |
| // "success" state so that HDD will immediately provide the trigger |
| // frame parameters to TL, who will in turn plumb them down to the |
| // Firmware. That way the Firmware will have the trigger frame |
| // parameters when it needs them |
| // just note that there is no longer an outstanding request |
| pSession->uapsdAlreadyRequested = VOS_FALSE; |
| } |
| |
| void sme_QosPmcOffloadStartUapsdCallback(void *callbackContext, |
| tANI_U32 sessionId, eHalStatus status) |
| { |
| sme_QosSessionInfo *pSession = callbackContext; |
| pSession->uapsdAlreadyRequested = VOS_FALSE; |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosPmcCheckRoutine() - Function registered with PMC to check with |
| SME-QoS whenever the device is about to enter one of the power |
| save modes. PMC runs a poll with all the registered modules if device can |
| enter power save mode or remain in full power |
| |
| \param callbackContext - The context passed to PMC during registration through |
| pmcRegisterPowerSaveCheck. |
| \return boolean |
| |
| SME-QOS returns PMC true or false respectively if it wants to vote for |
| entering power save or not |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| v_BOOL_t sme_QosPmcCheckRoutine(void *callbackContext) |
| { |
| sme_QosSessionInfo *pSession; |
| v_U8_t sessionId; |
| for (sessionId = 0; sessionId < CSR_ROAM_SESSION_MAX; ++sessionId) |
| { |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| if ((pSession->sessionActive) && |
| (!pSession->readyForPowerSave)) |
| { |
| return VOS_FALSE; |
| } |
| } |
| /* All active sessions have voted for power save */ |
| return VOS_TRUE; |
| } |
| |
| v_BOOL_t sme_QosPmcOffloadCheckRoutine(void *callbackContext, tANI_U32 sessionId) |
| { |
| sme_QosSessionInfo *pSession = &sme_QosCb.sessionInfo[sessionId]; |
| |
| if ((pSession->sessionActive) && |
| (!pSession->readyForPowerSave)) |
| { |
| return VOS_FALSE; |
| } |
| return VOS_TRUE; |
| |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosPmcDeviceStateUpdateInd() - Callback function registered with |
| PMC to notify SME-QoS when it changes the power state |
| |
| \param callbackContext - The context passed to PMC during registration |
| through pmcRegisterDeviceStateUpdateInd. |
| \param pmcState - Current power state that PMC moved into. |
| |
| \return None |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| void sme_QosPmcDeviceStateUpdateInd(void *callbackContext, tPmcState pmcState) |
| { |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| tpAniSirGlobal pMac = PMAC_STRUCT( callbackContext ); |
| //check all the entries in Flow list for non-zero service interval, which will |
| //tell us if we need to notify HDD when PMC is out of UAPSD mode or going |
| // back to UAPSD mode |
| switch(pmcState) |
| { |
| case FULL_POWER: |
| status = sme_QosProcessOutOfUapsdMode(pMac); |
| break; |
| case UAPSD: |
| status = sme_QosProcessIntoUapsdMode(pMac); |
| break; |
| default: |
| status = eHAL_STATUS_SUCCESS; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| "%s: %d: nothing to process in PMC state %s (%d)", |
| __func__, __LINE__, |
| sme_PmcStatetoString(pmcState), pmcState); |
| |
| } |
| if(!HAL_STATUS_SUCCESS(status)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: ignoring Device(PMC) state change to %s (%d)", |
| __func__, __LINE__, |
| sme_PmcStatetoString(pmcState), pmcState); |
| } |
| |
| } |
| |
| void sme_OffloadQosPmcDeviceStateUpdateInd(void *callbackContext, |
| tANI_U32 sessionId, tPmcState pmcState) |
| { |
| eHalStatus status = eHAL_STATUS_FAILURE; |
| tpAniSirGlobal pMac = PMAC_STRUCT( callbackContext ); |
| /* |
| * check all the entries in Flow list for non-zero service interval, |
| * which will tell us if we need to notify HDD when |
| * PMC is out of UAPSD mode or going |
| * back to UAPSD mode |
| */ |
| switch(pmcState) |
| { |
| case FULL_POWER: |
| status = sme_OffloadQosProcessOutOfUapsdMode(pMac, sessionId); |
| break; |
| case UAPSD: |
| status = sme_OffloadQosProcessIntoUapsdMode(pMac, sessionId); |
| break; |
| default: |
| status = eHAL_STATUS_SUCCESS; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: nothing to process in PMC state %d", |
| __func__, __LINE__, |
| pmcState); |
| } |
| if(!HAL_STATUS_SUCCESS(status)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: ignoring Device(PMC) state change to %d", |
| __func__, __LINE__, |
| pmcState); |
| } |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessOutOfUapsdMode() - Function to notify HDD when PMC |
| notifies SME-QoS that it moved out of UAPSD mode to FULL power |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessOutOfUapsdMode(tpAniSirGlobal pMac) |
| { |
| sme_QosSessionInfo *pSession; |
| tListElem *pEntry= NULL, *pNextEntry = NULL; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| |
| pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE ); |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Flow List empty, can't search", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| while( pEntry ) |
| { |
| pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE ); |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| pSession = &sme_QosCb.sessionInfo[flow_info->sessionId]; |
| //only notify the flows which already successfully setup UAPSD |
| if((flow_info->QoSInfo.max_service_interval || |
| flow_info->QoSInfo.min_service_interval) && |
| (SME_QOS_REASON_REQ_SUCCESS == flow_info->reason)) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pSession->ac_info[flow_info->ac_type].curr_QoSInfo[flow_info->tspec_mask - 1], |
| SME_QOS_STATUS_OUT_OF_APSD_POWER_MODE_IND, |
| flow_info->QosFlowID); |
| } |
| pEntry = pNextEntry; |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| eHalStatus sme_OffloadQosProcessOutOfUapsdMode(tpAniSirGlobal pMac, |
| tANI_U32 sessionId) |
| { |
| sme_QosSessionInfo *pSession; |
| tListElem *pEntry= NULL, *pNextEntry = NULL; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| |
| pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE ); |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Flow List empty, can't search", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| while(pEntry) |
| { |
| pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE ); |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| pSession = &sme_QosCb.sessionInfo[flow_info->sessionId]; |
| /* only notify the flows which already successfully setup UAPSD */ |
| if((sessionId == flow_info->sessionId) && |
| (flow_info->QoSInfo.max_service_interval || |
| flow_info->QoSInfo.min_service_interval) && |
| (SME_QOS_REASON_REQ_SUCCESS == flow_info->reason)) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pSession->ac_info[flow_info->ac_type]. |
| curr_QoSInfo[flow_info->tspec_mask - 1], |
| SME_QOS_STATUS_OUT_OF_APSD_POWER_MODE_IND, |
| flow_info->QosFlowID); |
| } |
| pEntry = pNextEntry; |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosProcessIntoUapsdMode() - Function to notify HDD when PMC |
| notifies SME-QoS that it is moving into UAPSD mode |
| |
| \param pMac - Pointer to the global MAC parameter structure. |
| \return eHalStatus |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| eHalStatus sme_QosProcessIntoUapsdMode(tpAniSirGlobal pMac) |
| { |
| sme_QosSessionInfo *pSession; |
| tListElem *pEntry= NULL, *pNextEntry = NULL; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| |
| pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE ); |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Flow List empty, can't search", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| while( pEntry ) |
| { |
| pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE ); |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| pSession = &sme_QosCb.sessionInfo[flow_info->sessionId]; |
| //only notify the flows which already successfully setup UAPSD |
| if( (flow_info->QoSInfo.ts_info.psb) && |
| (SME_QOS_REASON_REQ_SUCCESS == flow_info->reason) ) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pSession->ac_info[flow_info->ac_type].curr_QoSInfo[flow_info->tspec_mask - 1], |
| SME_QOS_STATUS_INTO_APSD_POWER_MODE_IND, |
| flow_info->QosFlowID); |
| } |
| pEntry = pNextEntry; |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| eHalStatus sme_OffloadQosProcessIntoUapsdMode(tpAniSirGlobal pMac, tANI_U32 sessionId) |
| { |
| sme_QosSessionInfo *pSession; |
| tListElem *pEntry= NULL, *pNextEntry = NULL; |
| sme_QosFlowInfoEntry *flow_info = NULL; |
| |
| pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE ); |
| if(!pEntry) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Flow List empty, can't search", |
| __func__, __LINE__); |
| return eHAL_STATUS_FAILURE; |
| } |
| while(pEntry) |
| { |
| pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE ); |
| flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link ); |
| pSession = &sme_QosCb.sessionInfo[flow_info->sessionId]; |
| /* only notify the flows which already successfully setup UAPSD */ |
| if((sessionId == flow_info->sessionId) && |
| (flow_info->QoSInfo.max_service_interval || |
| flow_info->QoSInfo.min_service_interval) && |
| (SME_QOS_REASON_REQ_SUCCESS == flow_info->reason)) |
| { |
| flow_info->QoSCallback(pMac, flow_info->HDDcontext, |
| &pSession->ac_info[flow_info->ac_type]. |
| curr_QoSInfo[flow_info->tspec_mask - 1], |
| SME_QOS_STATUS_INTO_APSD_POWER_MODE_IND, |
| flow_info->QosFlowID); |
| } |
| pEntry = pNextEntry; |
| } |
| return eHAL_STATUS_SUCCESS; |
| } |
| |
| void sme_QosCleanupCtrlBlkForHandoff(tpAniSirGlobal pMac, v_U8_t sessionId) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosEdcaAcType ac; |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| vos_mem_zero(pACInfo->curr_QoSInfo, |
| sizeof(sme_QosWmmTspecInfo) * SME_QOS_TSPEC_INDEX_MAX); |
| vos_mem_zero(pACInfo->requested_QoSInfo, |
| sizeof(sme_QosWmmTspecInfo) * SME_QOS_TSPEC_INDEX_MAX); |
| pACInfo->num_flows[0] = 0; |
| pACInfo->num_flows[1] = 0; |
| pACInfo->reassoc_pending = VOS_FALSE; |
| pACInfo->tspec_mask_status = 0; |
| pACInfo->tspec_pending = VOS_FALSE; |
| pACInfo->hoRenewal = VOS_FALSE; |
| pACInfo->prev_state = SME_QOS_LINK_UP; |
| } |
| } |
| |
| /*-------------------------------------------------------------------------- |
| \brief sme_QosIsTSInfoAckPolicyValid() - The SME QoS API exposed to HDD to |
| check if TS info ack policy field can be set to "HT-immediate block |
| acknowledgment" |
| |
| \param pMac - The handle returned by macOpen. |
| \param pQoSInfo - Pointer to sme_QosWmmTspecInfo which contains the WMM TSPEC |
| related info, provided by HDD |
| \param sessionId - sessionId returned by sme_OpenSession. |
| |
| \return VOS_TRUE - Current Association is HT association and so TS info ack policy |
| can be set to "HT-immediate block acknowledgment" |
| |
| \sa |
| |
| --------------------------------------------------------------------------*/ |
| v_BOOL_t sme_QosIsTSInfoAckPolicyValid(tpAniSirGlobal pMac, |
| sme_QosWmmTspecInfo * pQoSInfo, |
| v_U8_t sessionId) |
| { |
| tDot11fBeaconIEs *pIes = NULL; |
| sme_QosSessionInfo *pSession; |
| eHalStatus hstatus; |
| if( !CSR_IS_SESSION_VALID( pMac, sessionId ) ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Session Id %d is invalid", |
| __func__, __LINE__, |
| sessionId); |
| return VOS_FALSE; |
| } |
| |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| |
| if( !pSession->sessionActive ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Session %d is inactive", |
| __func__, __LINE__, |
| sessionId); |
| return VOS_FALSE; |
| } |
| |
| if(!pSession->assocInfo.pBssDesc) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Session %d has an Invalid BSS Descriptor", |
| __func__, __LINE__, |
| sessionId); |
| return VOS_FALSE; |
| } |
| |
| hstatus = csrGetParsedBssDescriptionIEs(pMac, |
| pSession->assocInfo.pBssDesc, |
| &pIes); |
| if(!HAL_STATUS_SUCCESS(hstatus)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d unable to parse BSS IEs", |
| __func__, __LINE__, |
| sessionId); |
| return VOS_FALSE; |
| } |
| |
| /* success means pIes was allocated */ |
| |
| if(!pIes->HTCaps.present && |
| pQoSInfo->ts_info.ack_policy == SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d HT Caps aren't present but application set ack policy to HT ", |
| __func__, __LINE__, |
| sessionId); |
| |
| vos_mem_free(pIes); |
| return VOS_FALSE; |
| } |
| |
| vos_mem_free(pIes); |
| return VOS_TRUE; |
| } |
| |
| v_BOOL_t sme_QosValidateRequestedParams(tpAniSirGlobal pMac, |
| sme_QosWmmTspecInfo * pQoSInfo, |
| v_U8_t sessionId) |
| { |
| v_BOOL_t rc = VOS_FALSE; |
| |
| do |
| { |
| if(SME_QOS_WMM_TS_DIR_RESV == pQoSInfo->ts_info.direction) break; |
| if(!sme_QosIsTSInfoAckPolicyValid(pMac, pQoSInfo, sessionId)) break; |
| |
| rc = VOS_TRUE; |
| }while(0); |
| return rc; |
| } |
| |
| static eHalStatus qosIssueCommand( tpAniSirGlobal pMac, v_U8_t sessionId, |
| eSmeCommandType cmdType, sme_QosWmmTspecInfo * pQoSInfo, |
| sme_QosEdcaAcType ac, v_U8_t tspec_mask ) |
| { |
| eHalStatus status = eHAL_STATUS_RESOURCES; |
| tSmeCmd *pCommand = NULL; |
| do |
| { |
| pCommand = smeGetCommandBuffer( pMac ); |
| if ( !pCommand ) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: fail to get command buffer for command %d", |
| __func__, __LINE__, cmdType); |
| break; |
| } |
| pCommand->command = cmdType; |
| pCommand->sessionId = sessionId; |
| switch ( cmdType ) |
| { |
| case eSmeCommandAddTs: |
| if( pQoSInfo ) |
| { |
| status = eHAL_STATUS_SUCCESS; |
| pCommand->u.qosCmd.tspecInfo = *pQoSInfo; |
| pCommand->u.qosCmd.ac = ac; |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: NULL pointer passed", |
| __func__, __LINE__); |
| status = eHAL_STATUS_INVALID_PARAMETER; |
| } |
| break; |
| case eSmeCommandDelTs: |
| status = eHAL_STATUS_SUCCESS; |
| pCommand->u.qosCmd.ac = ac; |
| pCommand->u.qosCmd.tspec_mask = tspec_mask; |
| break; |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: invalid command type %d", |
| __func__, __LINE__, cmdType ); |
| status = eHAL_STATUS_INVALID_PARAMETER; |
| break; |
| } |
| } while( 0 ); |
| if( HAL_STATUS_SUCCESS( status ) && pCommand ) |
| { |
| smePushCommand( pMac, pCommand, eANI_BOOLEAN_FALSE ); |
| } |
| else if( pCommand ) |
| { |
| qosReleaseCommand( pMac, pCommand ); |
| } |
| return( status ); |
| } |
| tANI_BOOLEAN qosProcessCommand( tpAniSirGlobal pMac, tSmeCmd *pCommand ) |
| { |
| eHalStatus status = eHAL_STATUS_SUCCESS; |
| tANI_BOOLEAN fRemoveCmd = eANI_BOOLEAN_TRUE; |
| do |
| { |
| switch ( pCommand->command ) |
| { |
| case eSmeCommandAddTs: |
| status = sme_QosAddTsReq( pMac, (v_U8_t)pCommand->sessionId, &pCommand->u.qosCmd.tspecInfo, pCommand->u.qosCmd.ac); |
| if( HAL_STATUS_SUCCESS( status ) ) |
| { |
| fRemoveCmd = eANI_BOOLEAN_FALSE; |
| status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP; |
| } |
| break; |
| case eSmeCommandDelTs: |
| status = sme_QosDelTsReq( pMac, (v_U8_t)pCommand->sessionId, pCommand->u.qosCmd.ac, pCommand->u.qosCmd.tspec_mask ); |
| if( HAL_STATUS_SUCCESS( status ) ) |
| { |
| fRemoveCmd = eANI_BOOLEAN_FALSE; |
| } |
| break; |
| default: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: invalid command type %d", |
| __func__, __LINE__, pCommand->command ); |
| break; |
| }//switch |
| } while(0); |
| return( fRemoveCmd ); |
| } |
| |
| /* |
| sme_QosTriggerUapsdChange |
| Invoked by BTC when UAPSD bypass is enabled or disabled |
| We, in turn, must disable or enable UAPSD on all flows as appropriate |
| That may require us to re-add TSPECs or to reassociate |
| */ |
| sme_QosStatusType sme_QosTriggerUapsdChange( tpAniSirGlobal pMac ) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| v_U8_t ac, tspec1 = 0, tspec2 = 0; |
| v_U8_t uapsd_mask; |
| tDot11fBeaconIEs *pIesLocal; |
| v_U8_t acm_mask; |
| v_BOOL_t fIsUapsdNeeded; |
| v_U8_t sessionId; |
| v_BOOL_t addtsWhenACMNotSet = CSR_IS_ADDTS_WHEN_ACMOFF_SUPPORTED(pMac); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Invoked", |
| __func__, __LINE__); |
| for (sessionId = 0; sessionId < CSR_ROAM_SESSION_MAX; ++sessionId) |
| { |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| if( !pSession->sessionActive ) |
| { |
| continue; |
| } |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Session %d is active", |
| __func__, __LINE__, |
| sessionId); |
| if( HAL_STATUS_SUCCESS(csrGetParsedBssDescriptionIEs(pMac, pSession->assocInfo.pBssDesc, &pIesLocal)) ) |
| { |
| // get the ACM mask |
| acm_mask = sme_QosGetACMMask(pMac, pSession->assocInfo.pBssDesc, pIesLocal); |
| vos_mem_free(pIesLocal); |
| // get the uapsd mask for this session |
| uapsd_mask = pSession->apsdMask; |
| // unmask the bits with ACM on to avoid reassoc on them |
| uapsd_mask &= ~acm_mask; |
| // iterate through the ACs to determine if we need to re-add any TSPECs |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| // Does this AC have QoS active? |
| if( SME_QOS_QOS_ON == pACInfo->curr_state ) |
| { |
| // Yes, QoS is active on this AC |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d AC %d has QoS active", |
| __func__, __LINE__, |
| sessionId, ac); |
| // Does this AC require ACM? |
| if(( acm_mask & (1 << (SME_QOS_EDCA_AC_VO - ac)) ) || addtsWhenACMNotSet ) |
| { |
| // Yes, so we need to re-add any TSPECS |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d AC %d has ACM enabled", |
| __func__, __LINE__, |
| sessionId, ac); |
| // Are any TSPECs active? |
| if( pACInfo->tspec_mask_status ) |
| { |
| // Yes, at least 1 TSPEC is active. Are they both active? |
| if( SME_QOS_TSPEC_MASK_BIT_1_2_SET == pACInfo->tspec_mask_status ) |
| { |
| //both TSPECS are active |
| tspec1 = SME_QOS_TSPEC_MASK_BIT_1_SET; |
| tspec2 = SME_QOS_TSPEC_MASK_BIT_2_SET; |
| } |
| else |
| { |
| // only one TSPEC is active, get its mask |
| tspec1 = SME_QOS_TSPEC_MASK_BIT_1_2_SET & pACInfo->tspec_mask_status; |
| } |
| // Does TSPEC 1 really require UAPSD? |
| fIsUapsdNeeded = (v_BOOL_t)(pACInfo->curr_QoSInfo[tspec1 - 1].ts_info.psb); |
| //double check whether we need to do anything |
| if( fIsUapsdNeeded ) |
| { |
| pACInfo->requested_QoSInfo[tspec1 - 1] = |
| pACInfo->curr_QoSInfo[tspec1 - 1]; |
| sme_QosReRequestAddTS( pMac, sessionId, |
| &pACInfo->requested_QoSInfo[tspec1 - 1], |
| ac, |
| tspec1 ); |
| } |
| // Is TSPEC 2 active? |
| if( tspec2 ) |
| { |
| // Does TSPEC 2 really require UAPSD? |
| fIsUapsdNeeded = (v_BOOL_t)(pACInfo->curr_QoSInfo[tspec2 - 1].ts_info.psb); |
| if( fIsUapsdNeeded ) |
| { |
| //No need to inform HDD |
| //pACInfo->hoRenewal = VOS_TRUE; |
| pACInfo->requested_QoSInfo[tspec2 - 1] = |
| pACInfo->curr_QoSInfo[tspec2 - 1]; |
| sme_QosReRequestAddTS( pMac, sessionId, |
| &pACInfo->requested_QoSInfo[tspec2 - 1], |
| ac, |
| tspec2); |
| } |
| } |
| } |
| else |
| { |
| // QoS is set, ACM is on, but no TSPECs -- inconsistent state |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d AC %d has QoS enabled and ACM is set, but no TSPEC", |
| __func__, __LINE__, |
| sessionId, ac); |
| VOS_ASSERT(0); |
| } |
| } |
| else |
| { |
| //Since ACM bit is not set, there should be only one QoS information for both directions. |
| fIsUapsdNeeded = (v_BOOL_t)(pACInfo->curr_QoSInfo[0].ts_info.psb); |
| if(fIsUapsdNeeded) |
| { |
| // we need UAPSD on this AC (and we may not currently have it) |
| uapsd_mask |= 1 << (SME_QOS_EDCA_AC_VO - ac); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: On session %d AC %d has ACM disabled, uapsd mask now 0x%X", |
| __func__, __LINE__, |
| sessionId, ac, uapsd_mask); |
| } |
| } |
| } |
| } |
| // do we need to reassociate? |
| if(uapsd_mask) |
| { |
| tCsrRoamModifyProfileFields modifyProfileFields; |
| //we need to do a reassoc on these AC |
| csrGetModifyProfileFields(pMac, sessionId, &modifyProfileFields); |
| modifyProfileFields.uapsd_mask = uapsd_mask; |
| //Do we need to inform HDD? |
| if(!HAL_STATUS_SUCCESS(sme_QosRequestReassoc(pMac, sessionId, &modifyProfileFields, VOS_TRUE))) |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On Session %d Reassoc failed", |
| __func__, __LINE__, |
| sessionId); |
| } |
| } |
| } |
| else |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On Session %d failed to parse IEs", |
| __func__, __LINE__, |
| sessionId); |
| } |
| } |
| // return status is ignored by BTC |
| return SME_QOS_STATUS_SETUP_SUCCESS_IND; |
| } |
| |
| /* |
| sme_QosReRequestAddTS to re-send AddTS for the combined QoS request |
| */ |
| static sme_QosStatusType sme_QosReRequestAddTS(tpAniSirGlobal pMac, |
| v_U8_t sessionId, |
| sme_QosWmmTspecInfo * pQoSInfo, |
| sme_QosEdcaAcType ac, |
| v_U8_t tspecMask) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| sme_QosStatusType status = SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| sme_QosCmdInfo cmd; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Invoked on session %d for AC %d TSPEC %d", |
| __func__, __LINE__, |
| sessionId, ac, tspecMask); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| pACInfo = &pSession->ac_info[ac]; |
| /* Need to vote off power save for the duration of this request */ |
| pSession->readyForPowerSave = VOS_FALSE; |
| /* |
| * Check to consider the following flowing scenario Addts request is pending |
| * on one AC, while APSD requested on another which needs a reassoc. |
| * Will buffer a request if Addts is pending on any AC, which will safeguard |
| * the above scenario, & also won't confuse PE with back to back Addts or |
| * Addts followed by Reassoc. |
| */ |
| if(!pMac->psOffloadEnabled) |
| { |
| if(sme_QosIsRspPending(sessionId, ac) || |
| ( eHAL_STATUS_PMC_PENDING == pmcRequestFullPower(pMac, |
| sme_QosPmcFullPowerCallback, pSession, eSME_REASON_OTHER))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d buffering the AddTS request " |
| "for AC %d in state %d as Addts is pending " |
| "on other AC or waiting for full power", |
| __func__, __LINE__, |
| sessionId, ac, pACInfo->curr_state); |
| //buffer cmd |
| cmd.command = SME_QOS_RESEND_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.resendCmdInfo.ac = ac; |
| cmd.u.resendCmdInfo.tspecMask = tspecMask; |
| cmd.u.resendCmdInfo.QoSInfo = *pQoSInfo; |
| if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_FALSE))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d unable to buffer the AddTS " |
| "request for AC %d TSPEC %d in state %d", |
| __func__, __LINE__, |
| sessionId, ac, tspecMask, pACInfo->curr_state); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| } |
| return SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; |
| } |
| } |
| else |
| { |
| if(sme_QosIsRspPending(sessionId, ac) || |
| (eHAL_STATUS_PMC_PENDING == pmcOffloadRequestFullPower(pMac, sessionId, |
| sme_QosPmcOffloadFullPowerCallback, pSession, eSME_REASON_OTHER))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d buffering the AddTS request " |
| "for AC %d in state %d as Addts is pending " |
| "on other AC or waiting for full power", |
| __func__, __LINE__, |
| sessionId, ac, pACInfo->curr_state); |
| //buffer cmd |
| cmd.command = SME_QOS_RESEND_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.resendCmdInfo.ac = ac; |
| cmd.u.resendCmdInfo.tspecMask = tspecMask; |
| cmd.u.resendCmdInfo.QoSInfo = *pQoSInfo; |
| if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_FALSE))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: On session %d unable to buffer the AddTS " |
| "request for AC %d TSPEC %d in state %d", |
| __func__, __LINE__, |
| sessionId, ac, tspecMask, pACInfo->curr_state); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| } |
| return SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; |
| } |
| } |
| |
| //get into the stat m/c to see if the request can be granted |
| switch(pACInfo->curr_state) |
| { |
| case SME_QOS_QOS_ON: |
| { |
| //if ACM, send out a new ADDTS |
| pACInfo->hoRenewal = VOS_TRUE; |
| status = sme_QosSetup(pMac, sessionId, pQoSInfo, ac); |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: sme_QosSetup returned in SME_QOS_QOS_ON state on " |
| "AC %d with status =%d", |
| __func__, __LINE__, |
| ac, status); |
| if(SME_QOS_STATUS_SETUP_REQ_PENDING_RSP != status) |
| { |
| /* We aren't waiting for a response from the AP |
| so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| } |
| if(SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status) |
| { |
| status = SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; |
| pACInfo->tspec_pending = tspecMask; |
| } |
| else if((SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) || |
| (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status) || |
| (SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING == status)) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: UAPSD is setup already status = %d " |
| "returned by sme_QosSetup", |
| __func__, __LINE__, |
| status); |
| } |
| else |
| { |
| //err msg |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: unexpected status = %d returned by sme_QosSetup", |
| __func__, __LINE__, |
| status); |
| } |
| } |
| break; |
| case SME_QOS_HANDOFF: |
| case SME_QOS_REQUESTED: |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: Re-Add request in state = %d buffer the request", |
| __func__, __LINE__, |
| pACInfo->curr_state); |
| cmd.command = SME_QOS_RESEND_REQ; |
| cmd.pMac = pMac; |
| cmd.sessionId = sessionId; |
| cmd.u.resendCmdInfo.ac = ac; |
| cmd.u.resendCmdInfo.tspecMask = tspecMask; |
| cmd.u.resendCmdInfo.QoSInfo = *pQoSInfo; |
| if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_FALSE))) |
| { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: couldn't buffer the read request in state = %d", |
| __func__, __LINE__, |
| pACInfo->curr_state ); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP; |
| } |
| status = SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP; |
| break; |
| case SME_QOS_CLOSED: |
| case SME_QOS_INIT: |
| case SME_QOS_LINK_UP: |
| default: |
| //print error msg, |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, |
| "%s: %d: ReAdd request in unexpected state = %d", |
| __func__, __LINE__, |
| pACInfo->curr_state ); |
| /* Unable to service the request |
| nothing is pending so vote power save back on */ |
| pSession->readyForPowerSave = VOS_TRUE; |
| // ASSERT? |
| break; |
| } |
| if((SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) || |
| (SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY == status)) |
| { |
| (void)sme_QosProcessBufferedCmd(sessionId); |
| } |
| return (status); |
| } |
| |
| static void sme_QosInitACs(tpAniSirGlobal pMac, v_U8_t sessionId) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosEdcaAcType ac; |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| vos_mem_zero(&pSession->ac_info[ac], sizeof(sme_QosACInfo)); |
| sme_QosStateTransition(sessionId, ac, SME_QOS_INIT); |
| } |
| } |
| static eHalStatus sme_QosRequestReassoc(tpAniSirGlobal pMac, tANI_U8 sessionId, |
| tCsrRoamModifyProfileFields *pModFields, |
| v_BOOL_t fForce ) |
| { |
| sme_QosSessionInfo *pSession; |
| sme_QosACInfo *pACInfo; |
| eHalStatus status; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: %d: Invoked on session %d with UAPSD mask 0x%X", |
| __func__, __LINE__, |
| sessionId, pModFields->uapsd_mask); |
| pSession = &sme_QosCb.sessionInfo[sessionId]; |
| status = csrReassoc(pMac, sessionId, pModFields, &pSession->roamID, fForce); |
| if(HAL_STATUS_SUCCESS(status)) |
| { |
| //Update the state to Handoff so subsequent requests are queued until |
| // this one is finished |
| sme_QosEdcaAcType ac; |
| for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) |
| { |
| pACInfo = &pSession->ac_info[ac]; |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, |
| "%s: %d: AC[%d] is in state [%d]", |
| __func__, __LINE__, |
| ac, pACInfo->curr_state ); |
| // If it is already in HANDOFF state, don't do anything since we |
| // MUST preserve the previous state and sme_QosStateTransition |
| // will change the previous state |
| if(SME_QOS_HANDOFF != pACInfo->curr_state) |
| { |
| sme_QosStateTransition(sessionId, ac, SME_QOS_HANDOFF); |
| } |
| } |
| } |
| return status; |
| } |
| static v_U32_t sme_QosAssignFlowId(void) |
| { |
| v_U32_t flowId; |
| flowId = sme_QosCb.nextFlowId; |
| if (SME_QOS_MAX_FLOW_ID == flowId) |
| { |
| // The Flow ID wrapped. This is obviously not a real life scenario |
| // but handle it to keep the software test folks happy |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL, |
| "%s: %d: Software Test made the flow counter wrap, " |
| "QoS may no longer be functional", |
| __func__, __LINE__); |
| sme_QosCb.nextFlowId = SME_QOS_MIN_FLOW_ID; |
| } |
| else |
| { |
| sme_QosCb.nextFlowId++; |
| } |
| return flowId; |
| } |
| |
| static v_U8_t sme_QosAssignDialogToken(void) |
| { |
| v_U8_t token; |
| token = sme_QosCb.nextDialogToken; |
| if (SME_QOS_MAX_DIALOG_TOKEN == token) |
| { |
| // wrap is ok |
| sme_QosCb.nextDialogToken = SME_QOS_MIN_DIALOG_TOKEN; |
| } |
| else |
| { |
| sme_QosCb.nextDialogToken++; |
| } |
| return token; |
| } |
| #endif /* WLAN_MDM_CODE_REDUCTION_OPT */ |