blob: 0b567bf7e0fe99c281edf9eee9e48ea9c32ea409 [file] [log] [blame]
/*
* 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