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
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: On session %d reassociation to enable "
"APSD on AC %d is pending",
__func__, __LINE__,
sessionId, ac);
status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP;
pACInfo->reassoc_pending = VOS_TRUE;
}
}
else
{
//err msg: no support for APSD from PMC
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: no support for APSD or BMPS from PMC",
__func__, __LINE__);
}
}
}while(0);
vos_mem_free(pIes);
return status;
}
#if defined(FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
/* This is a dummy function now. But the purpose of me adding this was to
* delay the TSPEC processing till SET_KEY completes. This function can be
* used to do any SME_QOS processing after the SET_KEY. As of now, it is
* not required as we are ok with tspec getting programmed before set_key
* as the roam timings are measured without tspec in reassoc!
*/
eHalStatus sme_QosProcessSetKeySuccessInd(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN,
"########### Set Key Complete #############");
(void)sme_QosProcessBufferedCmd(sessionId);
return eHAL_STATUS_SUCCESS;
}
#endif
#ifdef FEATURE_WLAN_ESE
/*--------------------------------------------------------------------------
\brief sme_QosESESaveTspecResponse() - This function saves the TSPEC
parameters that came along in the TSPEC IE in the reassoc response
\param pMac - Pointer to the global MAC parameter structure.
\param sessionId - SME session ID
\param pTspec - Pointer to the TSPEC IE from the reassoc rsp
\param ac - Access Category for which this TSPEC rsp is received
\param tspecIndex - flow/direction
\return eHAL_STATUS_SUCCESS - Release is successful.
--------------------------------------------------------------------------*/
eHalStatus sme_QosESESaveTspecResponse(tpAniSirGlobal pMac, v_U8_t sessionId, tDot11fIEWMMTSPEC *pTspec, v_U8_t ac, v_U8_t tspecIndex)
{
tpSirAddtsRsp pAddtsRsp = &sme_QosCb.sessionInfo[sessionId].ac_info[ac].addTsRsp[tspecIndex];
ac = sme_QosUPtoACMap[pTspec->user_priority];
vos_mem_zero(pAddtsRsp, sizeof(tSirAddtsRsp));
pAddtsRsp->messageType = eWNI_SME_ADDTS_RSP;
pAddtsRsp->length = sizeof(tSirAddtsRsp);
pAddtsRsp->rc = eSIR_SUCCESS;
pAddtsRsp->sessionId = sessionId;
pAddtsRsp->rsp.dialogToken = 0;
pAddtsRsp->rsp.status = eSIR_SUCCESS;
pAddtsRsp->rsp.wmeTspecPresent = pTspec->present;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
"%s: Copy Tspec to local data structure ac=%d, tspecIdx=%d",
__func__, ac, tspecIndex);
if (pAddtsRsp->rsp.wmeTspecPresent)
{
//Copy TSPEC params received in assoc response to addts response
ConvertWMMTSPEC(pMac, &pAddtsRsp->rsp.tspec, pTspec);
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosESEProcessReassocTspecRsp() - This function processes the
WMM TSPEC IE in the reassoc response. Reassoc triggered as part of
ESE roaming to another ESE capable AP. If the TSPEC was added before
reassoc, as part of Call Admission Control, the reassoc req from the
STA would carry the TSPEC parameters which were already negotiated
with the older AP.
\param pMac - Pointer to the global MAC parameter structure.
\param sessionId - SME session ID
\param pEven_info - Pointer to the smeJoinRsp structure
\return eHAL_STATUS_SUCCESS - Release is successful.
--------------------------------------------------------------------------*/
eHalStatus sme_QosESEProcessReassocTspecRsp(tpAniSirGlobal pMac, v_U8_t sessionId, void* pEvent_info)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
tDot11fIEWMMTSPEC *pTspecIE = NULL;
tCsrRoamSession *pCsrSession = NULL;
tCsrRoamConnectedInfo *pCsrConnectedInfo = NULL;
eHalStatus status = eHAL_STATUS_FAILURE;
v_U8_t ac, numTspec, cnt;
v_U8_t tspec_flow_index, tspec_mask_status;
v_U32_t tspecIeLen;
pCsrSession = CSR_GET_SESSION(pMac, sessionId);
if (NULL == pCsrSession) {
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("session %d not found"), sessionId);
return eHAL_STATUS_FAILURE;
}
pCsrConnectedInfo = &pCsrSession->connectedInfo;
pSession = &sme_QosCb.sessionInfo[sessionId];
// Get the TSPEC IEs which came along with the reassoc response
// from the pbFrames pointer
pTspecIE = (tDot11fIEWMMTSPEC *)(pCsrConnectedInfo->pbFrames + pCsrConnectedInfo->nBeaconLength +
pCsrConnectedInfo->nAssocReqLength + pCsrConnectedInfo->nAssocRspLength + pCsrConnectedInfo->nRICRspLength);
/* Get the number of tspecs Ies in the frame, the min length
should be at-least equal to the one TSPEC IE */
tspecIeLen = pCsrConnectedInfo->nTspecIeLength;
if (tspecIeLen < sizeof(tDot11fIEWMMTSPEC)) {
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("ESE Tspec IE len %d less than min %zu"),
tspecIeLen, sizeof(tDot11fIEWMMTSPEC));
return eHAL_STATUS_FAILURE;
}
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN,
"TspecLen = %d, pbFrames = %pK, pTspecIE = %pK",
tspecIeLen, pCsrConnectedInfo->pbFrames, pTspecIE);
numTspec = (tspecIeLen)/sizeof(tDot11fIEWMMTSPEC);
for(cnt=0; cnt<numTspec; cnt++) {
ac = sme_QosUpToAc(pTspecIE->user_priority);
if (ac >= SME_QOS_EDCA_AC_MAX) {
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("ac %d more than it`s max value"), ac);
return eHAL_STATUS_FAILURE;
}
pACInfo = &pSession->ac_info[ac];
tspec_mask_status = pACInfo->tspec_mask_status;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN,
FL("UP=%d, ac=%d, tspec_mask_status=%x"),
pTspecIE->user_priority, ac, tspec_mask_status );
for (tspec_flow_index = 0; tspec_flow_index < SME_QOS_TSPEC_INDEX_MAX; tspec_flow_index++) {
if (tspec_mask_status & (1 << tspec_flow_index)) {
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN,
FL("Found Tspec entry flow = %d AC = %d"),tspec_flow_index, ac);
sme_QosESESaveTspecResponse(pMac, sessionId, pTspecIE, ac, tspec_flow_index);
} else {
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN,
FL("Not found Tspec entry flow = %d AC = %d"),tspec_flow_index, ac);
}
}
// Increment the pointer to point it to the next TSPEC IE
pTspecIE++;
}
/* Send the Aggregated QoS request to HAL */
status = sme_QosFTAggrQosReq(pMac,sessionId);
return status;
}
/*--------------------------------------------------------------------------
\brief sme_QosCopyTspecInfo() - This function copies the existing TSPEC
parameters from the source structure to the destination structure.
\param pMac - Pointer to the global MAC parameter structure.
\param pTspec_Info - source structure
\param pTspec - destination structure
\return void
--------------------------------------------------------------------------*/
static void sme_QosCopyTspecInfo(tpAniSirGlobal pMac, sme_QosWmmTspecInfo *pTspec_Info, tSirMacTspecIE* pTspec)
{
/* As per WMM_AC_testplan_v0.39 Minimum Service Interval, Maximum Service
* Interval, Service Start Time, Suspension Interval and Delay Bound are
* all intended for HCCA operation and therefore must be set to zero*/
pTspec->delayBound = pTspec_Info->delay_bound;
pTspec->inactInterval = pTspec_Info->inactivity_interval;
pTspec->length = SME_QOS_TSPEC_IE_LENGTH;
pTspec->maxBurstSz = pTspec_Info->max_burst_size;
pTspec->maxMsduSz = pTspec_Info->maximum_msdu_size;
pTspec->maxSvcInterval = pTspec_Info->max_service_interval;
pTspec->meanDataRate = pTspec_Info->mean_data_rate;
pTspec->mediumTime = pTspec_Info->medium_time;
pTspec->minDataRate = pTspec_Info->min_data_rate;
pTspec->minPhyRate = pTspec_Info->min_phy_rate;
pTspec->minSvcInterval = pTspec_Info->min_service_interval;
pTspec->nomMsduSz = pTspec_Info->nominal_msdu_size;
pTspec->peakDataRate = pTspec_Info->peak_data_rate;
pTspec->surplusBw = pTspec_Info->surplus_bw_allowance;
pTspec->suspendInterval = pTspec_Info->suspension_interval;
pTspec->svcStartTime = pTspec_Info->svc_start_time;
pTspec->tsinfo.traffic.direction = pTspec_Info->ts_info.direction;
pTspec->tsinfo.traffic.psb = pTspec_Info->ts_info.psb;
pTspec->tsinfo.traffic.tsid = pTspec_Info->ts_info.tid;
pTspec->tsinfo.traffic.userPrio = pTspec_Info->ts_info.up;
pTspec->tsinfo.traffic.accessPolicy = SME_QOS_ACCESS_POLICY_EDCA;
pTspec->tsinfo.traffic.burstSizeDefn = pTspec_Info->ts_info.burst_size_defn;
pTspec->tsinfo.traffic.ackPolicy = pTspec_Info->ts_info.ack_policy;
pTspec->type = SME_QOS_TSPEC_IE_TYPE;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: up = %d, tid = %d",
__func__, __LINE__,
pTspec_Info->ts_info.up,
pTspec_Info->ts_info.tid);
}
/*--------------------------------------------------------------------------
\brief sme_QosEseRetrieveTspecInfo() - This function is called by CSR
when try to create reassoc request message to PE - csrSendSmeReassocReqMsg
This functions get the existing tspec parameters to be included
in the reassoc request.
\param pMac - Pointer to the global MAC parameter structure.
\param sessionId - SME session ID
\param pTspecInfo - Pointer to the structure to carry back the TSPEC parameters
\return v_U8_t - number of existing negotiated TSPECs
--------------------------------------------------------------------------*/
v_U8_t sme_QosEseRetrieveTspecInfo(tpAniSirGlobal pMac, v_U8_t sessionId, tTspecInfo *pTspecInfo)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
v_U8_t tspec_mask_status = 0;
v_U8_t tspec_pending_status = 0;
v_U8_t ac, numTspecs = 0;
tTspecInfo *pDstTspec = pTspecInfo;
//TODO: Check if TSPEC has already been established, if not return
pSession = &sme_QosCb.sessionInfo[sessionId];
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++) {
volatile v_U8_t tspec_index = 0;
pACInfo = &pSession->ac_info[ac];
tspec_pending_status = pACInfo->tspec_pending;
tspec_mask_status = pACInfo->tspec_mask_status;
do {
if (tspec_mask_status & SME_QOS_TSPEC_MASK_BIT_1_SET) {
/* If a tspec status is pending, take requested_QoSInfo for RIC request, else use curr_QoSInfo
for the RIC request */
if (tspec_pending_status & SME_QOS_TSPEC_MASK_BIT_1_SET) {
sme_QosCopyTspecInfo(pMac, &pACInfo->requested_QoSInfo[tspec_index], &pDstTspec->tspec);
} else {
sme_QosCopyTspecInfo(pMac, &pACInfo->curr_QoSInfo[tspec_index], &pDstTspec->tspec);
}
pDstTspec->valid = TRUE;
numTspecs++;
pDstTspec++;
}
tspec_mask_status >>= 1;
tspec_pending_status >>= 1;
tspec_index++;
} while (tspec_mask_status);
}
return numTspecs;
}
#endif
#ifdef WLAN_FEATURE_VOWIFI_11R
eHalStatus sme_QosCreateTspecRICIE(tpAniSirGlobal pMac, sme_QosWmmTspecInfo *pTspec_Info,
v_U8_t *pRICBuffer, v_U32_t *pRICLength, v_U8_t *pRICIdentifier)
{
tDot11fIERICDataDesc ricIE;
tANI_U32 nStatus;
if (pRICBuffer == NULL || pRICIdentifier == NULL || pRICLength == NULL)
{
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
vos_mem_zero(&ricIE, sizeof(tDot11fIERICDataDesc));
ricIE.present = 1;
ricIE.RICData.present = 1;
ricIE.RICData.resourceDescCount = 1;
ricIE.RICData.statusCode = 0;
ricIE.RICData.Identifier = sme_QosAssignDialogToken();
#ifndef USE_80211_WMMTSPEC_FOR_RIC
ricIE.TSPEC.present = 1;
ricIE.TSPEC.delay_bound = pTspec_Info->delay_bound;
ricIE.TSPEC.inactivity_int = pTspec_Info->inactivity_interval;
ricIE.TSPEC.burst_size = pTspec_Info->max_burst_size;
ricIE.TSPEC.max_msdu_size = pTspec_Info->maximum_msdu_size;
ricIE.TSPEC.max_service_int = pTspec_Info->max_service_interval;
ricIE.TSPEC.mean_data_rate = pTspec_Info->mean_data_rate;
ricIE.TSPEC.medium_time = 0;
ricIE.TSPEC.min_data_rate = pTspec_Info->min_data_rate;
ricIE.TSPEC.min_phy_rate = pTspec_Info->min_phy_rate;
ricIE.TSPEC.min_service_int = pTspec_Info->min_service_interval;
ricIE.TSPEC.size = pTspec_Info->nominal_msdu_size;
ricIE.TSPEC.peak_data_rate = pTspec_Info->peak_data_rate;
ricIE.TSPEC.surplus_bw_allowance = pTspec_Info->surplus_bw_allowance;
ricIE.TSPEC.suspension_int = pTspec_Info->suspension_interval;
ricIE.TSPEC.service_start_time = pTspec_Info->svc_start_time;
ricIE.TSPEC.direction = pTspec_Info->ts_info.direction;
ricIE.TSPEC.psb = pTspec_Info->ts_info.psb;
ricIE.TSPEC.tsid = pTspec_Info->ts_info.tid;
ricIE.TSPEC.user_priority = pTspec_Info->ts_info.up;
ricIE.TSPEC.access_policy = SME_QOS_ACCESS_POLICY_EDCA;
*pRICIdentifier = ricIE.RICData.Identifier;
nStatus = dot11fPackIeRICDataDesc(pMac, &ricIE, pRICBuffer, sizeof(ricIE), pRICLength);
if (DOT11F_FAILED(nStatus))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("Packing of RIC Data of length %d failed with status %d"),
*pRICLength, nStatus);
}
#else // WMM TSPEC
/*As per WMM_AC_testplan_v0.39 Minimum Service Interval, Maximum Service
Interval, Service Start Time, Suspension Interval and Delay Bound are
all intended for HCCA operation and therefore must be set to zero*/
ricIE.WMMTSPEC.present = 1;
ricIE.WMMTSPEC.version = 1;
ricIE.WMMTSPEC.delay_bound = pTspec_Info->delay_bound;
ricIE.WMMTSPEC.inactivity_int = pTspec_Info->inactivity_interval;
ricIE.WMMTSPEC.burst_size = pTspec_Info->max_burst_size;
ricIE.WMMTSPEC.max_msdu_size = pTspec_Info->maximum_msdu_size;
ricIE.WMMTSPEC.max_service_int = pTspec_Info->max_service_interval;
ricIE.WMMTSPEC.mean_data_rate = pTspec_Info->mean_data_rate;
ricIE.WMMTSPEC.medium_time = 0;
ricIE.WMMTSPEC.min_data_rate = pTspec_Info->min_data_rate;
ricIE.WMMTSPEC.min_phy_rate = pTspec_Info->min_phy_rate;
ricIE.WMMTSPEC.min_service_int = pTspec_Info->min_service_interval;
ricIE.WMMTSPEC.size = pTspec_Info->nominal_msdu_size;
ricIE.WMMTSPEC.peak_data_rate = pTspec_Info->peak_data_rate;
ricIE.WMMTSPEC.surplus_bw_allowance = pTspec_Info->surplus_bw_allowance;
ricIE.WMMTSPEC.suspension_int = pTspec_Info->suspension_interval;
ricIE.WMMTSPEC.service_start_time = pTspec_Info->svc_start_time;
ricIE.WMMTSPEC.direction = pTspec_Info->ts_info.direction;
ricIE.WMMTSPEC.psb = pTspec_Info->ts_info.psb;
ricIE.WMMTSPEC.tsid = pTspec_Info->ts_info.tid;
ricIE.WMMTSPEC.user_priority = pTspec_Info->ts_info.up;
ricIE.WMMTSPEC.access_policy = SME_QOS_ACCESS_POLICY_EDCA;
nStatus = dot11fPackIeRICDataDesc(pMac, &ricIE, pRICBuffer, sizeof(ricIE), pRICLength);
if (DOT11F_FAILED(nStatus))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("Packing of RIC Data of length %d failed with status %d"),
*pRICLength, nStatus);
}
#endif /* 80211_TSPEC */
*pRICIdentifier = ricIE.RICData.Identifier;
return nStatus;
}
eHalStatus sme_QosProcessFTReassocReqEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
v_U8_t ac, qos_requested = FALSE;
v_U8_t tspec_flow_index;
sme_QosFlowInfoEntry *flow_info = NULL;
tListElem *pEntry= NULL;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
FL("Invoked on session %d"), sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
qos_requested = FALSE;
for (tspec_flow_index = 0; tspec_flow_index < SME_QOS_TSPEC_INDEX_MAX; tspec_flow_index++)
{
/* Only in the below case, copy the AC's curr QoS Info to requested QoS info */
if ((pACInfo->ricIdentifier[tspec_flow_index] && !pACInfo->tspec_pending) ||
(pACInfo->tspec_mask_status & (1<<tspec_flow_index)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
FL("Copying the currentQos to requestedQos for AC=%d, flow=%d"),
ac, tspec_flow_index );
pACInfo->requested_QoSInfo[tspec_flow_index] = pACInfo->curr_QoSInfo[tspec_flow_index];
vos_mem_zero(&pACInfo->curr_QoSInfo[tspec_flow_index], sizeof(sme_QosWmmTspecInfo));
qos_requested = TRUE;
}
}
// Only if the tspec is required, transition the state to
// SME_QOS_REQUESTED for this AC
if (qos_requested)
{
switch(pACInfo->curr_state)
{
case SME_QOS_HANDOFF:
sme_QosStateTransition(sessionId, ac, SME_QOS_REQUESTED);
break;
default:
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("FT Reassoc req event in unexpected state %d"), pACInfo->curr_state);
VOS_ASSERT(0);
}
}
}
/* At this point of time, we are disconnected from the old AP, so it is safe
* to reset all these session variables */
pSession->apsdMask = 0;
pSession->uapsdAlreadyRequested = 0;
pSession->readyForPowerSave = 0;
/* Now change reason and HO renewal of all the flow in this session only */
pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE );
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN,
"%s: %d: Flow List empty, nothing to update",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
do
{
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
if(sessionId == flow_info->sessionId)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Changing FlowID %d reason to SETUP and HO renewal to FALSE",
__func__, __LINE__,
flow_info->QosFlowID);
flow_info->reason = SME_QOS_REASON_SETUP;
flow_info->hoRenewal = eANI_BOOLEAN_TRUE;
}
pEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE );
} while( pEntry );
return eHAL_STATUS_SUCCESS;
}
eHalStatus sme_QosFTAggrQosReq( tpAniSirGlobal pMac, v_U8_t sessionId )
{
tSirAggrQosReq *pMsg = NULL;
sme_QosSessionInfo *pSession;
eHalStatus status = eHAL_STATUS_FAILURE;
int i, j = 0;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d", __func__, __LINE__,
sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
pMsg = (tSirAggrQosReq *)vos_mem_malloc(sizeof(tSirAggrQosReq));
if (!pMsg)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: couldn't allocate memory for the msg buffer",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
vos_mem_zero(pMsg, sizeof(tSirAggrQosReq));
pMsg->messageType = pal_cpu_to_be16((v_U16_t)eWNI_SME_FT_AGGR_QOS_REQ);
pMsg->length = sizeof(tSirAggrQosReq);
pMsg->sessionId = sessionId;
pMsg->timeout = 0;
pMsg->rspReqd = VOS_TRUE;
vos_mem_copy( &pMsg->bssId[ 0 ],
&pSession->assocInfo.pBssDesc->bssId[ 0 ],
sizeof(tCsrBssid) );
for( i = 0; i < SME_QOS_EDCA_AC_MAX; i++ )
{
for( j = 0; j < SME_QOS_TSPEC_INDEX_MAX; j++ )
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
FL("ac=%d, tspec_mask_staus=%x, tspec_index=%d"),
i, pSession->ac_info[i].tspec_mask_status, j);
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
FL("direction = %d"), pSession->ac_info[i].addTsRsp[j].rsp.tspec.tsinfo.traffic.direction);
// Check if any flow is active on this AC
if ((pSession->ac_info[i].tspec_mask_status) & (1 << j))
{
tANI_U8 direction = pSession->ac_info[i].addTsRsp[j].rsp.tspec.tsinfo.traffic.direction;
if ((direction == SME_QOS_WMM_TS_DIR_UPLINK) ||
(direction == SME_QOS_WMM_TS_DIR_BOTH))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN,
FL("Found tspec entry AC=%d, flow=%d, direction = %d"), i, j, direction);
pMsg->aggrInfo.aggrAddTsInfo[i].dialogToken =
sme_QosAssignDialogToken();
pMsg->aggrInfo.aggrAddTsInfo[i].lleTspecPresent =
pSession->ac_info[i].addTsRsp[j].rsp.lleTspecPresent;
pMsg->aggrInfo.aggrAddTsInfo[i].numTclas =
pSession->ac_info[i].addTsRsp[j].rsp.numTclas;
vos_mem_copy( pMsg->aggrInfo.aggrAddTsInfo[i].tclasInfo,
pSession->ac_info[i].addTsRsp[j].rsp.tclasInfo,
SIR_MAC_TCLASIE_MAXNUM );
pMsg->aggrInfo.aggrAddTsInfo[i].tclasProc =
pSession->ac_info[i].addTsRsp[j].rsp.tclasProc;
pMsg->aggrInfo.aggrAddTsInfo[i].tclasProcPresent =
pSession->ac_info[i].addTsRsp[j].rsp.tclasProcPresent;
pMsg->aggrInfo.aggrAddTsInfo[i].tspec =
pSession->ac_info[i].addTsRsp[j].rsp.tspec;
pMsg->aggrInfo.aggrAddTsInfo[i].wmeTspecPresent =
pSession->ac_info[i].addTsRsp[j].rsp.wmeTspecPresent;
pMsg->aggrInfo.aggrAddTsInfo[i].wsmTspecPresent =
pSession->ac_info[i].addTsRsp[j].rsp.wsmTspecPresent;
pMsg->aggrInfo.tspecIdx |= ( 1 << i );
// Mark the index for this AC as pending for response, which would be
// used to validate the AddTS response from HAL->PE->SME
pSession->ac_info[i].tspec_pending = (1<<j);
}
}
}
}
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
"Sending aggregated message to HAL 0x%x", pMsg->aggrInfo.tspecIdx);
if(HAL_STATUS_SUCCESS(palSendMBMessage(pMac->hHdd, pMsg)))
{
status = eHAL_STATUS_SUCCESS;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: sent down a AGGR QoS req to PE",
__func__, __LINE__);
}
return status;
}
eHalStatus sme_QosProcessFTRICResponse(tpAniSirGlobal pMac, v_U8_t sessionId, tDot11fIERICDataDesc *pRicDataDesc, v_U8_t ac, v_U8_t tspecIndex)
{
tANI_U8 i = 0;
tpSirAddtsRsp pAddtsRsp
= &sme_QosCb.sessionInfo[sessionId].ac_info[ac].addTsRsp[tspecIndex];
vos_mem_zero(pAddtsRsp, sizeof(tSirAddtsRsp));
pAddtsRsp->messageType = eWNI_SME_ADDTS_RSP;
pAddtsRsp->length = sizeof(tSirAddtsRsp);
pAddtsRsp->rc = pRicDataDesc->RICData.statusCode;
pAddtsRsp->sessionId = sessionId;
pAddtsRsp->rsp.dialogToken = pRicDataDesc->RICData.Identifier;
pAddtsRsp->rsp.status = pRicDataDesc->RICData.statusCode;
pAddtsRsp->rsp.wmeTspecPresent = pRicDataDesc->TSPEC.present;
if (pAddtsRsp->rsp.wmeTspecPresent)
{
//Copy TSPEC params received in RIC response to addts response
ConvertTSPEC(pMac, &pAddtsRsp->rsp.tspec, &pRicDataDesc->TSPEC);
}
pAddtsRsp->rsp.numTclas = pRicDataDesc->num_TCLAS;
if (pAddtsRsp->rsp.numTclas)
{
for (i = 0; i < pAddtsRsp->rsp.numTclas; i++)
{
//Copy TCLAS info per index to the addts response
ConvertTCLAS(pMac, &pAddtsRsp->rsp.tclasInfo[i], &pRicDataDesc->TCLAS[i]);
}
}
pAddtsRsp->rsp.tclasProcPresent = pRicDataDesc->TCLASSPROC.present;
if (pAddtsRsp->rsp.tclasProcPresent)
pAddtsRsp->rsp.tclasProc = pRicDataDesc->TCLASSPROC.processing;
pAddtsRsp->rsp.schedulePresent = pRicDataDesc->Schedule.present;
if (pAddtsRsp->rsp.schedulePresent)
{
//Copy Schedule IE params to addts response
ConvertSchedule(pMac, &pAddtsRsp->rsp.schedule, &pRicDataDesc->Schedule);
}
//Need to check the below portion is a part of WMM TSPEC
//Process Delay element
if (pRicDataDesc->TSDelay.present)
ConvertTSDelay(pMac, &pAddtsRsp->rsp.delay, &pRicDataDesc->TSDelay);
//Need to call for WMMTSPEC
if (pRicDataDesc->WMMTSPEC.present)
{
ConvertWMMTSPEC(pMac, &pAddtsRsp->rsp.tspec, &pRicDataDesc->WMMTSPEC);
}
//return sme_QosProcessAddTsRsp(pMac, &addtsRsp);
return eHAL_STATUS_SUCCESS;
}
eHalStatus sme_QosProcessAggrQosRsp(tpAniSirGlobal pMac, void *pMsgBuf)
{
tpSirAggrQosRsp pAggrRsp = (tpSirAggrQosRsp)pMsgBuf;
tSirAddtsRsp addtsRsp;
eHalStatus status = eHAL_STATUS_SUCCESS;
int i, j = 0;
tANI_U8 sessionId = pAggrRsp->sessionId;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
FL("Received AGGR_QOS resp from LIM"));
/* Copy over the updated response information for TSPEC of all the ACs */
for( i = 0; i < SIR_QOS_NUM_AC_MAX; i++ )
{
tANI_U8 tspec_mask_status = sme_QosCb.sessionInfo[sessionId].ac_info[i].tspec_mask_status;
for( j = 0; j < SME_QOS_TSPEC_INDEX_MAX; j++ )
{
tANI_U8 direction = sme_QosCb.sessionInfo[sessionId].ac_info[i].
addTsRsp[j].rsp.tspec.tsinfo.traffic.direction;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
FL("Addts rsp from LIM AC=%d, flow=%d dir=%d, tspecIdx=%x"),
i, j, direction, pAggrRsp->aggrInfo.tspecIdx);
// Check if the direction is Uplink or bi-directional
if( ((1<<i) & pAggrRsp->aggrInfo.tspecIdx) &&
((tspec_mask_status) & (1<<j)) &&
((direction == SME_QOS_WMM_TS_DIR_UPLINK) ||
(direction == SME_QOS_WMM_TS_DIR_BOTH)))
{
addtsRsp = sme_QosCb.sessionInfo[sessionId].ac_info[i].addTsRsp[j];
addtsRsp.rc = pAggrRsp->aggrInfo.aggrRsp[i].status;
addtsRsp.rsp.status = pAggrRsp->aggrInfo.aggrRsp[i].status;
addtsRsp.rsp.tspec = pAggrRsp->aggrInfo.aggrRsp[i].tspec;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
FL("Processing Addts rsp from LIM AC=%d, flow=%d"), i, j);
/* post ADD TS response for each */
if (sme_QosProcessAddTsRsp(pMac, &addtsRsp) != eHAL_STATUS_SUCCESS)
{
status = eHAL_STATUS_FAILURE;
}
}
}
}
return status;
}
eHalStatus sme_QosProcessFTReassocRspEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
v_U8_t ac;
v_U8_t tspec_flow_index;
tDot11fIERICDataDesc *pRicDataDesc = NULL;
eHalStatus status = eHAL_STATUS_SUCCESS;
tCsrRoamSession *pCsrSession = CSR_GET_SESSION( pMac, sessionId );
tCsrRoamConnectedInfo *pCsrConnectedInfo = NULL;
tANI_U32 ricRspLen;
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
tDot11fIERICDataDesc *pRicData = NULL;
tANI_U32 ricLen;
v_BOOL_t Found = false;
sme_QosWmmDirType direction;
v_U8_t ac1;
#endif
if (NULL == pCsrSession)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("The Session pointer is NULL"));
return eHAL_STATUS_FAILURE;
}
pCsrConnectedInfo = &pCsrSession->connectedInfo;
ricRspLen = pCsrConnectedInfo->nRICRspLength;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d",
__func__, __LINE__,
sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
pRicDataDesc = (tDot11fIERICDataDesc *)((pCsrConnectedInfo->pbFrames) +
(pCsrConnectedInfo->nBeaconLength + pCsrConnectedInfo->nAssocReqLength +
pCsrConnectedInfo->nAssocRspLength));
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
if(!pCsrSession->roamOffloadSynchParams.bRoamSynchInProgress)
{
#endif
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
for (tspec_flow_index = 0; tspec_flow_index < SME_QOS_TSPEC_INDEX_MAX; tspec_flow_index++)
{
/* Only in the below case, copy the AC's curr QoS Info to requested QoS info */
if (pACInfo->ricIdentifier[tspec_flow_index])
{
if (!ricRspLen)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("RIC Response not received for AC %d on TSPEC Index %d, RIC Req Identifier = %d"),
ac, tspec_flow_index, pACInfo->ricIdentifier[tspec_flow_index]);
VOS_ASSERT(0);
}
else
{
/* Now we got response for this identifier. Process it. */
if (pRicDataDesc->present)
{
if (pRicDataDesc->RICData.present)
{
if (pRicDataDesc->RICData.Identifier != pACInfo->ricIdentifier[tspec_flow_index])
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("RIC response order not same as request sent. Request ID = %d, Response ID = %d"),
pACInfo->ricIdentifier[tspec_flow_index], pRicDataDesc->RICData.Identifier);
VOS_ASSERT(0);
}
else
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
FL("Processing RIC Response for AC %d, TSPEC Flow index %d with RIC ID %d "),
ac, tspec_flow_index, pRicDataDesc->RICData.Identifier);
status = sme_QosProcessFTRICResponse(pMac, sessionId, pRicDataDesc, ac, tspec_flow_index);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("Failed with status %d for AC %d in TSPEC Flow index = %d"),
status, ac, tspec_flow_index);
}
}
pRicDataDesc++;
ricRspLen -= sizeof(tDot11fIERICDataDesc);
}
}
}
}
}
}
if (ricRspLen)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("RIC Response still follows despite traversing through all ACs. Remaining len = %d"), ricRspLen);
VOS_ASSERT(0);
}
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
}
else
{
/* It means LFR3.0 roaming with RIC,
* currently we have support for WMM TSPEC alone
* In LFR3.0 11r since we do not have a RIC identifier
* maintained in host so identify the tspec from the AC
* and direction info */
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
FL("LFR3-11r Compare RIC in Reassoc Resp to find"
" matching tspec in host."));
pRicData = pRicDataDesc;
ricLen = ricRspLen;
if (ricRspLen && pRicDataDesc->present &&
pRicDataDesc->WMMTSPEC.present) {
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
for (tspec_flow_index = 0;
tspec_flow_index < SME_QOS_TSPEC_INDEX_MAX;
tspec_flow_index++) {
if((pSession->ac_info[ac].tspec_mask_status)
& (1 << tspec_flow_index)) {
do {
ac1 = sme_QosUpToAc(pRicData->WMMTSPEC.user_priority);
if (ac == SME_QOS_EDCA_AC_MAX) {
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("Invalid AC %d UP %d"), ac,
pRicData->WMMTSPEC.user_priority);
break;
}
direction = pRicData->WMMTSPEC.direction;
if (ac == ac1 &&
direction == pACInfo->requested_QoSInfo[tspec_flow_index].ts_info.direction)
{
/* It means we found a matching tspec */
Found = true;
status = sme_QosProcessFTRICResponse(pMac,
sessionId,
pRicData,
ac,
tspec_flow_index);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("Failed with status %d for AC %d in TSPEC Flow index = %d"),
status, ac, tspec_flow_index);
}
break;
}
pRicData++;
ricLen -= sizeof(tDot11fIERICDataDesc);
}while(ricLen);
}
pRicData = pRicDataDesc;
ricLen = ricRspLen;
Found = false;
}
}
}else {
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
FL("LFR3-11r ricRspLen is zero or pRicDataDesc is not"
" present or wmmtspec is not present"));
}
}
#endif
/* Send the Aggregated QoS request to HAL */
status = sme_QosFTAggrQosReq(pMac,sessionId);
return status;
}
#endif /* WLAN_FEATURE_VOWIFI_11R */
/*--------------------------------------------------------------------------
\brief sme_QosAddTsReq() - To send down the ADDTS request with TSPEC params
to PE
\param pMac - Pointer to the global MAC parameter structure.
\param sessionId - Session upon which the TSPEC should be added
\param pTspec_Info - Pointer to sme_QosWmmTspecInfo which contains the WMM
TSPEC related info as defined above
\param ac - Enumeration of the various EDCA Access Categories.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosAddTsReq(tpAniSirGlobal pMac,
v_U8_t sessionId,
sme_QosWmmTspecInfo * pTspec_Info,
sme_QosEdcaAcType ac)
{
tSirAddtsReq *pMsg = NULL;
sme_QosSessionInfo *pSession;
eHalStatus status = eHAL_STATUS_FAILURE;
#ifdef FEATURE_WLAN_ESE
tCsrRoamSession *pCsrSession = CSR_GET_SESSION( pMac, sessionId );
#endif
#ifdef FEATURE_WLAN_DIAG_SUPPORT
WLAN_VOS_DIAG_EVENT_DEF(qos, vos_event_wlan_qos_payload_type);
#endif
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d for AC %d",
__func__, __LINE__,
sessionId, ac);
if (sessionId >= CSR_ROAM_SESSION_MAX)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: sessionId(%d) is invalid",
__func__, __LINE__, sessionId);
return eHAL_STATUS_FAILURE;
}
pSession = &sme_QosCb.sessionInfo[sessionId];
pMsg = (tSirAddtsReq *)vos_mem_malloc(sizeof(tSirAddtsReq));
if (!pMsg)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: couldn't allocate memory for the msg buffer",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
vos_mem_zero(pMsg, sizeof(tSirAddtsReq));
pMsg->messageType = pal_cpu_to_be16((v_U16_t)eWNI_SME_ADDTS_REQ);
pMsg->length = sizeof(tSirAddtsReq);
pMsg->sessionId = sessionId;
pMsg->timeout = 0;
pMsg->rspReqd = VOS_TRUE;
pMsg->req.dialogToken = sme_QosAssignDialogToken();
/*As per WMM_AC_testplan_v0.39 Minimum Service Interval, Maximum Service
Interval, Service Start Time, Suspension Interval and Delay Bound are
all intended for HCCA operation and therefore must be set to zero*/
pMsg->req.tspec.delayBound = 0;
pMsg->req.tspec.inactInterval = pTspec_Info->inactivity_interval;
pMsg->req.tspec.length = SME_QOS_TSPEC_IE_LENGTH;
pMsg->req.tspec.maxBurstSz = pTspec_Info->max_burst_size;
pMsg->req.tspec.maxMsduSz = pTspec_Info->maximum_msdu_size;
pMsg->req.tspec.maxSvcInterval = pTspec_Info->max_service_interval;
pMsg->req.tspec.meanDataRate = pTspec_Info->mean_data_rate;
pMsg->req.tspec.mediumTime = pTspec_Info->medium_time;
pMsg->req.tspec.minDataRate = pTspec_Info->min_data_rate;
pMsg->req.tspec.minPhyRate = pTspec_Info->min_phy_rate;
pMsg->req.tspec.minSvcInterval = pTspec_Info->min_service_interval;
pMsg->req.tspec.nomMsduSz = pTspec_Info->nominal_msdu_size;
pMsg->req.tspec.peakDataRate = pTspec_Info->peak_data_rate;
pMsg->req.tspec.surplusBw = pTspec_Info->surplus_bw_allowance;
pMsg->req.tspec.suspendInterval = pTspec_Info->suspension_interval;
pMsg->req.tspec.svcStartTime = 0;
pMsg->req.tspec.tsinfo.traffic.direction = pTspec_Info->ts_info.direction;
//Make sure UAPSD is allowed. BTC may want to disable UAPSD while keep QoS setup
if( pTspec_Info->ts_info.psb)
{
pMsg->req.tspec.tsinfo.traffic.psb = pTspec_Info->ts_info.psb;
}
else
{
pMsg->req.tspec.tsinfo.traffic.psb = 0;
pTspec_Info->ts_info.psb = 0;
}
pMsg->req.tspec.tsinfo.traffic.tsid = pTspec_Info->ts_info.tid;
pMsg->req.tspec.tsinfo.traffic.userPrio = pTspec_Info->ts_info.up;
pMsg->req.tspec.tsinfo.traffic.accessPolicy = SME_QOS_ACCESS_POLICY_EDCA;
pMsg->req.tspec.tsinfo.traffic.burstSizeDefn = pTspec_Info->ts_info.burst_size_defn;
pMsg->req.tspec.tsinfo.traffic.ackPolicy = pTspec_Info->ts_info.ack_policy;
pMsg->req.tspec.type = SME_QOS_TSPEC_IE_TYPE;
/*Fill the BSSID pMsg->req.bssId*/
if (NULL == pSession->assocInfo.pBssDesc)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: BSS descriptor is NULL so we don't send request to PE",
__func__, __LINE__);
vos_mem_free(pMsg);
return eHAL_STATUS_FAILURE;
}
vos_mem_copy( &pMsg->bssId[ 0 ],
&pSession->assocInfo.pBssDesc->bssId[ 0 ],
sizeof(tCsrBssid) );
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: up = %d, tid = %d",
__func__, __LINE__,
pTspec_Info->ts_info.up,
pTspec_Info->ts_info.tid);
#ifdef FEATURE_WLAN_ESE
if(pCsrSession->connectedProfile.isESEAssoc)
{
pMsg->req.tsrsIE.tsid = pTspec_Info->ts_info.up;
pMsg->req.tsrsPresent = 1;
}
#endif
if(HAL_STATUS_SUCCESS(palSendMBMessage(pMac->hHdd, pMsg)))
{
status = eHAL_STATUS_SUCCESS;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: sent down a ADDTS req to PE",
__func__, __LINE__);
//event: EVENT_WLAN_QOS
#ifdef FEATURE_WLAN_DIAG_SUPPORT
qos.eventId = SME_QOS_DIAG_ADDTS_REQ;
qos.reasonCode = SME_QOS_DIAG_USER_REQUESTED;
WLAN_VOS_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS);
#endif //FEATURE_WLAN_DIAG_SUPPORT
}
return status;
}
/*--------------------------------------------------------------------------
\brief sme_QosDelTsReq() - To send down the DELTS request with TSPEC params
to PE
\param pMac - Pointer to the global MAC parameter structure.
\param sessionId - Session from which the TSPEC should be deleted
\param ac - Enumeration of the various EDCA Access Categories.
\param tspec_mask - on which tspec per AC, the delts is requested
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosDelTsReq(tpAniSirGlobal pMac,
v_U8_t sessionId,
sme_QosEdcaAcType ac,
v_U8_t tspec_mask)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
tSirDeltsReq *pMsg;
sme_QosWmmTspecInfo *pTspecInfo;
eHalStatus status = eHAL_STATUS_FAILURE;
#ifdef FEATURE_WLAN_DIAG_SUPPORT
WLAN_VOS_DIAG_EVENT_DEF(qos, vos_event_wlan_qos_payload_type);
#endif
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d for AC %d",
__func__, __LINE__,
sessionId, ac);
pMsg = (tSirDeltsReq *)vos_mem_malloc(sizeof(tSirDeltsReq));
if (!pMsg)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: couldn't allocate memory for the msg buffer",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
vos_mem_zero(pMsg, sizeof(tSirDeltsReq));
// get pointer to the TSPEC being deleted
pSession = &sme_QosCb.sessionInfo[sessionId];
pACInfo = &pSession->ac_info[ac];
pTspecInfo = &pACInfo->curr_QoSInfo[tspec_mask - 1];
pMsg->messageType = pal_cpu_to_be16((v_U16_t)eWNI_SME_DELTS_REQ);
pMsg->length = sizeof(tSirDeltsReq);
pMsg->sessionId = sessionId;
pMsg->rspReqd = VOS_TRUE;
pMsg->req.tspec.delayBound = pTspecInfo->delay_bound;
pMsg->req.tspec.inactInterval = pTspecInfo->inactivity_interval;
pMsg->req.tspec.length = SME_QOS_TSPEC_IE_LENGTH;
pMsg->req.tspec.maxBurstSz = pTspecInfo->max_burst_size;
pMsg->req.tspec.maxMsduSz = pTspecInfo->maximum_msdu_size;
pMsg->req.tspec.maxSvcInterval = pTspecInfo->max_service_interval;
pMsg->req.tspec.meanDataRate = pTspecInfo->mean_data_rate;
pMsg->req.tspec.mediumTime = pTspecInfo->medium_time;
pMsg->req.tspec.minDataRate = pTspecInfo->min_data_rate;
pMsg->req.tspec.minPhyRate = pTspecInfo->min_phy_rate;
pMsg->req.tspec.minSvcInterval = pTspecInfo->min_service_interval;
pMsg->req.tspec.nomMsduSz = pTspecInfo->nominal_msdu_size;
pMsg->req.tspec.peakDataRate = pTspecInfo->peak_data_rate;
pMsg->req.tspec.surplusBw = pTspecInfo->surplus_bw_allowance;
pMsg->req.tspec.suspendInterval = pTspecInfo->suspension_interval;
pMsg->req.tspec.svcStartTime = pTspecInfo->svc_start_time;
pMsg->req.tspec.tsinfo.traffic.direction = pTspecInfo->ts_info.direction;
pMsg->req.tspec.tsinfo.traffic.psb = pTspecInfo->ts_info.psb;
pMsg->req.tspec.tsinfo.traffic.tsid = pTspecInfo->ts_info.tid;
pMsg->req.tspec.tsinfo.traffic.userPrio = pTspecInfo->ts_info.up;
pMsg->req.tspec.tsinfo.traffic.accessPolicy = SME_QOS_ACCESS_POLICY_EDCA;
pMsg->req.tspec.tsinfo.traffic.burstSizeDefn = pTspecInfo->ts_info.burst_size_defn;
pMsg->req.tspec.tsinfo.traffic.ackPolicy = pTspecInfo->ts_info.ack_policy;
pMsg->req.tspec.type = SME_QOS_TSPEC_IE_TYPE;
/*Fill the BSSID pMsg->req.bssId*/
if (NULL == pSession->assocInfo.pBssDesc)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: BSS descriptor is NULL so we don't send request to PE",
__func__, __LINE__);
vos_mem_free(pMsg);
return eHAL_STATUS_FAILURE;
}
vos_mem_copy( &pMsg->bssId[ 0 ],
&pSession->assocInfo.pBssDesc->bssId[ 0 ],
sizeof(tCsrBssid) );
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: up = %d, tid = %d",
__func__, __LINE__,
pTspecInfo->ts_info.up,
pTspecInfo->ts_info.tid);
vos_mem_zero(&pACInfo->curr_QoSInfo[tspec_mask - 1],
sizeof(sme_QosWmmTspecInfo));
if(HAL_STATUS_SUCCESS(palSendMBMessage(pMac->hHdd, pMsg)))
{
status = eHAL_STATUS_SUCCESS;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: sme_QosDelTsReq:Test: sent down a DELTS req to PE",
__func__, __LINE__);
//event: EVENT_WLAN_QOS
#ifdef FEATURE_WLAN_DIAG_SUPPORT
qos.eventId = SME_QOS_DIAG_DELTS;
qos.reasonCode = SME_QOS_DIAG_USER_REQUESTED;
WLAN_VOS_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS);
#endif //FEATURE_WLAN_DIAG_SUPPORT
}
return status;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessAddTsRsp() - Function to process the
eWNI_SME_ADDTS_RSP came from PE
\param pMac - Pointer to the global MAC parameter structure.
\param pMsgBuf - Pointer to the msg buffer came from PE.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessAddTsRsp(tpAniSirGlobal pMac, void *pMsgBuf)
{
tpSirAddtsRsp paddts_rsp = (tpSirAddtsRsp)pMsgBuf;
sme_QosSessionInfo *pSession;
v_U8_t sessionId = paddts_rsp->sessionId;
eHalStatus status = eHAL_STATUS_FAILURE;
#ifdef WLAN_FEATURE_VOWIFI_11R
sme_QosWmmUpType up = (sme_QosWmmUpType)paddts_rsp->rsp.tspec.tsinfo.traffic.userPrio;
sme_QosACInfo *pACInfo;
sme_QosEdcaAcType ac;
#endif
#ifdef FEATURE_WLAN_DIAG_SUPPORT
WLAN_VOS_DIAG_EVENT_DEF(qos, vos_event_wlan_qos_payload_type);
#endif
pSession = &sme_QosCb.sessionInfo[sessionId];
#ifdef WLAN_FEATURE_VOWIFI_11R
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d for UP %d",
__func__, __LINE__,
sessionId, up);
ac = sme_QosUpToAc(up);
if(SME_QOS_EDCA_AC_MAX == ac)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: invalid AC %d from UP %d",
__func__, __LINE__, ac, up);
return eHAL_STATUS_FAILURE;
}
pACInfo = &pSession->ac_info[ac];
if (SME_QOS_HANDOFF == pACInfo->curr_state)
{
smsLog(pMac, LOG1, FL("ADDTS Response received for AC %d in HANDOFF State.. Dropping"), ac);
pSession->readyForPowerSave = VOS_TRUE;
return eHAL_STATUS_SUCCESS;
}
#endif
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Invoked on session %d with return code %d",
__func__, __LINE__,
sessionId, paddts_rsp->rc);
/* Our outstanding request has been serviced
we can go into power save */
pSession->readyForPowerSave = VOS_TRUE;
if(paddts_rsp->rc)
{
//event: EVENT_WLAN_QOS
#ifdef FEATURE_WLAN_DIAG_SUPPORT
qos.eventId = SME_QOS_DIAG_ADDTS_RSP;
qos.reasonCode = SME_QOS_DIAG_ADDTS_REFUSED;
WLAN_VOS_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS);
#endif //FEATURE_WLAN_DIAG_SUPPORT
status = sme_QosProcessAddTsFailureRsp(pMac, sessionId, &paddts_rsp->rsp);
}
else
{
status = sme_QosProcessAddTsSuccessRsp(pMac, sessionId, &paddts_rsp->rsp);
}
return status;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessDelTsRsp() - Function to process the
eWNI_SME_DELTS_RSP came from PE
\param pMac - Pointer to the global MAC parameter structure.
\param pMsgBuf - Pointer to the msg buffer came from PE.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessDelTsRsp(tpAniSirGlobal pMac, void *pMsgBuf)
{
tpSirDeltsRsp pDeltsRsp = (tpSirDeltsRsp)pMsgBuf;
sme_QosSessionInfo *pSession;
v_U8_t sessionId = pDeltsRsp->sessionId;
// msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Invoked on session %d with return code %d",
__func__, __LINE__,
sessionId, pDeltsRsp->rc);
pSession = &sme_QosCb.sessionInfo[sessionId];
/* Our outstanding request has been serviced
we can go into power save */
pSession->readyForPowerSave = VOS_TRUE;
(void)sme_QosProcessBufferedCmd(sessionId);
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessDelTsInd() - Function to process the
eWNI_SME_DELTS_IND came from PE
Since it's a DELTS indication from AP, will notify all the flows running on
this AC about QoS release
\param pMac - Pointer to the global MAC parameter structure.
\param pMsgBuf - Pointer to the msg buffer came from PE.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessDelTsInd(tpAniSirGlobal pMac, void *pMsgBuf)
{
tpSirDeltsRsp pdeltsind = (tpSirDeltsRsp)pMsgBuf;
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
v_U8_t sessionId = pdeltsind->sessionId;
sme_QosEdcaAcType ac;
sme_QosSearchInfo search_key;
sme_QosWmmUpType up = (sme_QosWmmUpType)pdeltsind->rsp.tspec.tsinfo.traffic.userPrio;
#ifdef FEATURE_WLAN_DIAG_SUPPORT
WLAN_VOS_DIAG_EVENT_DEF(qos, vos_event_wlan_qos_payload_type);
#endif
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Invoked on session %d for UP %d",
__func__, __LINE__,
sessionId, up);
ac = sme_QosUpToAc(up);
if(SME_QOS_EDCA_AC_MAX == ac)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: invalid AC %d from UP %d",
__func__, __LINE__,
ac, up);
return eHAL_STATUS_FAILURE;
}
pSession = &sme_QosCb.sessionInfo[sessionId];
pACInfo = &pSession->ac_info[ac];
vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo));
//set the key type & the key to be searched in the Flow List
search_key.key.ac_type = ac;
search_key.index = SME_QOS_SEARCH_KEY_INDEX_2;
search_key.sessionId = sessionId;
/*
* Find all Flows on the particular AC & delete them, also send HDD
* indication through the callback it registered per request
*/
if(!HAL_STATUS_SUCCESS(sme_QosFindAllInFlowList(pMac, search_key, sme_QosDelTsIndFnp)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: no match found for ac = %d",
__func__, __LINE__,
search_key.key.ac_type);
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
//event: EVENT_WLAN_QOS
#ifdef FEATURE_WLAN_DIAG_SUPPORT
qos.eventId = SME_QOS_DIAG_DELTS;
qos.reasonCode = SME_QOS_DIAG_DELTS_IND_FROM_AP;
WLAN_VOS_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS);
#endif //FEATURE_WLAN_DIAG_SUPPORT
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessAssocCompleteEv() - Function to process the
SME_QOS_CSR_ASSOC_COMPLETE event indication from CSR
\param pEvent_info - Pointer to relevant info from CSR.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessAssocCompleteEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
eHalStatus status = eHAL_STATUS_FAILURE;
sme_QosEdcaAcType ac = SME_QOS_EDCA_AC_BE;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d",
__func__, __LINE__,
sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
if(((SME_QOS_INIT == pSession->ac_info[SME_QOS_EDCA_AC_BE].curr_state)&&
(SME_QOS_INIT == pSession->ac_info[SME_QOS_EDCA_AC_BK].curr_state)&&
(SME_QOS_INIT == pSession->ac_info[SME_QOS_EDCA_AC_VI].curr_state)&&
(SME_QOS_INIT == pSession->ac_info[SME_QOS_EDCA_AC_VO].curr_state)) ||
(pSession->handoffRequested))
{
//get the association info
if(!pEvent_info)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: pEvent_info is NULL",
__func__, __LINE__);
return status;
}
if(!((sme_QosAssocInfo *)pEvent_info)->pBssDesc)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: pBssDesc is NULL",
__func__, __LINE__);
return status;
}
if((pSession->assocInfo.pBssDesc) &&
(csrIsBssidMatch(pMac, (tCsrBssid *)&pSession->assocInfo.pBssDesc->bssId,
(tCsrBssid *) &(((sme_QosAssocInfo *)pEvent_info)->pBssDesc->bssId))))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: assoc with the same BSS, no update needed",
__func__, __LINE__);
}
else
{
status = sme_QosSaveAssocInfo(pSession, pEvent_info);
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: wrong state: BE %d, BK %d, VI %d, VO %d",
__func__, __LINE__,
pSession->ac_info[SME_QOS_EDCA_AC_BE].curr_state,
pSession->ac_info[SME_QOS_EDCA_AC_BK].curr_state,
pSession->ac_info[SME_QOS_EDCA_AC_VI].curr_state,
pSession->ac_info[SME_QOS_EDCA_AC_VO].curr_state);
VOS_ASSERT(0);
return status;
}
// the session is active
pSession->sessionActive = VOS_TRUE;
if(pSession->handoffRequested)
{
pSession->handoffRequested = VOS_FALSE;
//renew all flows
(void)sme_QosProcessBufferedCmd(sessionId);
status = eHAL_STATUS_SUCCESS;
}
else
{
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
switch(pACInfo->curr_state)
{
case SME_QOS_INIT:
sme_QosStateTransition(sessionId, ac, SME_QOS_LINK_UP);
break;
case SME_QOS_LINK_UP:
case SME_QOS_REQUESTED:
case SME_QOS_QOS_ON:
case SME_QOS_HANDOFF:
case SME_QOS_CLOSED:
default:
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d AC %d is in wrong state %d",
__func__, __LINE__,
sessionId, ac, pACInfo->curr_state);
VOS_ASSERT(0);
break;
}
}
}
return status;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessReassocReqEv() - Function to process the
SME_QOS_CSR_REASSOC_REQ event indication from CSR
\param pEvent_info - Pointer to relevant info from CSR.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessReassocReqEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosEdcaAcType ac;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d",
__func__, __LINE__,
sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
#ifdef WLAN_FEATURE_VOWIFI_11R
if(pSession->ftHandoffInProgress)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: no need for state transition, should "
"already be in handoff state",
__func__, __LINE__);
if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[3].curr_state != SME_QOS_HANDOFF))
{
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
sme_QosProcessFTReassocReqEv(pMac, sessionId, pEvent_info);
return eHAL_STATUS_SUCCESS;
}
#endif
if(pSession->handoffRequested)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: no need for state transition, should "
"already be in handoff state",
__func__, __LINE__);
if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[3].curr_state != SME_QOS_HANDOFF))
{
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
//buffer the existing flows to be renewed after handoff is done
sme_QosBufferExistingFlows(pMac, sessionId);
//clean up the control block partially for handoff
sme_QosCleanupCtrlBlkForHandoff(pMac, sessionId);
return eHAL_STATUS_SUCCESS;
}
//TBH: Assuming both handoff algo & 11r willn't be enabled at the same time
#ifdef WLAN_FEATURE_VOWIFI_11R
if(pSession->ftHandoffInProgress)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: no need for state transition, should "
"already be in handoff state",
__func__, __LINE__);
if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[3].curr_state != SME_QOS_HANDOFF))
{
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
sme_QosProcessFTReassocReqEv(pMac, sessionId, pEvent_info);
return eHAL_STATUS_SUCCESS;
}
#endif
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
switch(pACInfo->curr_state)
{
case SME_QOS_LINK_UP:
case SME_QOS_REQUESTED:
case SME_QOS_QOS_ON:
sme_QosStateTransition(sessionId, ac, SME_QOS_HANDOFF);
break;
case SME_QOS_HANDOFF:
//This is normal because sme_QosRequestReassoc may already change the state
break;
case SME_QOS_CLOSED:
case SME_QOS_INIT:
default:
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d AC %d is in wrong state %d",
__func__, __LINE__,
sessionId, ac, pACInfo->curr_state);
VOS_ASSERT(0);
break;
}
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessReassocSuccessEv() - Function to process the
SME_QOS_CSR_REASSOC_COMPLETE event indication from CSR
\param pEvent_info - Pointer to relevant info from CSR.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessReassocSuccessEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info)
{
tCsrRoamSession *pCsrRoamSession = NULL;
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosEdcaAcType ac, ac_index;
sme_QosSearchInfo search_key;
sme_QosSearchInfo search_key1;
eHalStatus status = eHAL_STATUS_FAILURE;
tListElem *pEntry= NULL;
sme_QosFlowInfoEntry *flow_info = NULL;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d",
__func__, __LINE__,
sessionId);
if (CSR_ROAM_SESSION_MAX <= sessionId) {
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: invoked on session %d",
__func__, __LINE__,
sessionId);
return status;
}
pCsrRoamSession = CSR_GET_SESSION( pMac, sessionId );
pSession = &sme_QosCb.sessionInfo[sessionId];
/* Our pending reassociation has completed
we can allow power save */
pSession->readyForPowerSave = VOS_TRUE;
//get the association info
if(!pEvent_info)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: pEvent_info is NULL",
__func__, __LINE__);
return status;
}
if(!((sme_QosAssocInfo *)pEvent_info)->pBssDesc)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: pBssDesc is NULL",
__func__, __LINE__);
return status;
}
status = sme_QosSaveAssocInfo(pSession, pEvent_info);
if(status)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: sme_QosSaveAssocInfo() failed",
__func__, __LINE__);
}
//TBH: Assuming both handoff algo & 11r willn't be enabled at the same time
if(pSession->handoffRequested)
{
pSession->handoffRequested = VOS_FALSE;
//renew all flows
(void)sme_QosProcessBufferedCmd(sessionId);
return eHAL_STATUS_SUCCESS;
}
#ifdef WLAN_FEATURE_VOWIFI_11R
if (pSession->ftHandoffInProgress)
{
if (csrRoamIs11rAssoc(pMac, sessionId)) {
if (pCsrRoamSession &&
pCsrRoamSession->connectedInfo.nRICRspLength) {
status = sme_QosProcessFTReassocRspEv(pMac, sessionId,
pEvent_info);
}
}
#ifdef FEATURE_WLAN_ESE
// If ESE association check for TSPEC IEs in the reassoc rsp frame
if (csrRoamIsESEAssoc(pMac, sessionId)) {
if (pCsrRoamSession &&
pCsrRoamSession->connectedInfo.nTspecIeLength) {
status = sme_QosESEProcessReassocTspecRsp(pMac, sessionId,
pEvent_info);
}
}
#endif
pSession->ftHandoffInProgress = VOS_FALSE;
pSession->handoffRequested = VOS_FALSE;
return status;
}
#endif
pSession->sessionActive = VOS_TRUE;
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
switch(pACInfo->curr_state)
{
case SME_QOS_HANDOFF:
// return to our previous state
sme_QosStateTransition(sessionId, ac, pACInfo->prev_state);
//for which ac APSD (hence the reassoc) is requested
if(pACInfo->reassoc_pending)
{
//update the apsd mask in CB - make sure to take care of the case
//where we are resetting the bit in apsd_mask
if(pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0].ts_info.psb)
{
pSession->apsdMask |= 1 << (SME_QOS_EDCA_AC_VO - ac);
}
else
{
pSession->apsdMask &= ~(1 << (SME_QOS_EDCA_AC_VO - ac));
}
pACInfo->reassoc_pending = VOS_FALSE;
//during setup it gets set as addts & reassoc both gets a pending flag
//pACInfo->tspec_pending = 0;
sme_QosStateTransition(sessionId, ac, SME_QOS_QOS_ON);
// notify HDD with new Service Interval
pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0] =
pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0];
vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo));
//set the key type & the key to be searched in the Flow List
search_key.key.ac_type = ac;
search_key.index = SME_QOS_SEARCH_KEY_INDEX_2;
search_key.sessionId = sessionId;
//notify PMC that reassoc is done for APSD on certain AC??
vos_mem_zero(&search_key1, sizeof(sme_QosSearchInfo));
//set the hoRenewal field in control block if needed
search_key1.index = SME_QOS_SEARCH_KEY_INDEX_3;
search_key1.key.reason = SME_QOS_REASON_SETUP;
search_key1.sessionId = sessionId;
for(ac_index = SME_QOS_EDCA_AC_BE; ac_index < SME_QOS_EDCA_AC_MAX; ac_index++)
{
pEntry = sme_QosFindInFlowList(search_key1);
if(pEntry)
{
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
if(flow_info->ac_type == ac)
{
pACInfo->hoRenewal = flow_info->hoRenewal;
break;
}
}
}
//notify HDD the success for the requested flow
//notify all the other flows running on the AC that QoS got modified
if(!HAL_STATUS_SUCCESS(sme_QosFindAllInFlowList(pMac, search_key, sme_QosReassocSuccessEvFnp)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: no match found for ac = %d",
__func__, __LINE__,
search_key.key.ac_type);
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
pACInfo->hoRenewal = VOS_FALSE;
vos_mem_zero(&pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0],
sizeof(sme_QosWmmTspecInfo));
}
status = eHAL_STATUS_SUCCESS;
break;
case SME_QOS_INIT:
case SME_QOS_CLOSED:
//NOP
status = eHAL_STATUS_SUCCESS;
break;
case SME_QOS_LINK_UP:
case SME_QOS_REQUESTED:
case SME_QOS_QOS_ON:
default:
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d AC %d is in wrong state %d",
__func__, __LINE__,
sessionId, ac, pACInfo->curr_state);
VOS_ASSERT(0);
break;
}
}
(void)sme_QosProcessBufferedCmd(sessionId);
return status;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessReassocFailureEv() - Function to process the
SME_QOS_CSR_REASSOC_FAILURE event indication from CSR
\param pEvent_info - Pointer to relevant info from CSR.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessReassocFailureEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosEdcaAcType ac;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d",
__func__, __LINE__,
sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
/* Our pending reassociation has completed
we can allow power save */
pSession->readyForPowerSave = VOS_TRUE;
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
switch(pACInfo->curr_state)
{
case SME_QOS_HANDOFF:
sme_QosStateTransition(sessionId, ac, SME_QOS_INIT);
if(pACInfo->reassoc_pending)
{
pACInfo->reassoc_pending = VOS_FALSE;
}
vos_mem_zero(&pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0],
sizeof(sme_QosWmmTspecInfo));
vos_mem_zero(&pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0],
sizeof(sme_QosWmmTspecInfo));
vos_mem_zero(&pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_1],
sizeof(sme_QosWmmTspecInfo));
vos_mem_zero(&pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_1],
sizeof(sme_QosWmmTspecInfo));
pACInfo->tspec_mask_status = SME_QOS_TSPEC_MASK_CLEAR;
pACInfo->tspec_pending = 0;
pACInfo->num_flows[SME_QOS_TSPEC_INDEX_0] = 0;
pACInfo->num_flows[SME_QOS_TSPEC_INDEX_1] = 0;
break;
case SME_QOS_INIT:
case SME_QOS_CLOSED:
//NOP
break;
case SME_QOS_LINK_UP:
case SME_QOS_REQUESTED:
case SME_QOS_QOS_ON:
default:
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d AC %d is in wrong state %d",
__func__, __LINE__,
sessionId, ac, pACInfo->curr_state);
VOS_ASSERT(0);
break;
}
}
//need to clean up flows
sme_QosDeleteExistingFlows(pMac, sessionId);
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessHandoffAssocReqEv() - Function to process the
SME_QOS_CSR_HANDOFF_ASSOC_REQ event indication from CSR
\param pEvent_info - Pointer to relevant info from CSR.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessHandoffAssocReqEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
v_U8_t ac;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d",
__func__, __LINE__,
sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
switch(pACInfo->curr_state)
{
case SME_QOS_LINK_UP:
case SME_QOS_REQUESTED:
case SME_QOS_QOS_ON:
sme_QosStateTransition(sessionId, ac, SME_QOS_HANDOFF);
break;
case SME_QOS_HANDOFF:
//print error msg
#ifdef WLAN_FEATURE_VOWIFI_11R
if(pSession->ftHandoffInProgress)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
"%s: %d: SME_QOS_CSR_HANDOFF_ASSOC_REQ received in "
"SME_QOS_HANDOFF state with FT in progress"
, __func__, __LINE__);
break;
}
#endif
case SME_QOS_CLOSED:
case SME_QOS_INIT:
default:
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d AC %d is in wrong state %d",
__func__, __LINE__,
sessionId, ac, pACInfo->curr_state);
VOS_ASSERT(0);
break;
}
}
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
if (csrRoamIs11rAssoc(pMac, sessionId)) {
/* Need not check here if it is LFR3.0 roaming,
* since ftHandoffInProgress will be true if it
* is 11r assoc even with LFR2.0 */
pSession->ftHandoffInProgress = VOS_TRUE;
}
#endif
// If FT handoff is in progress, legacy handoff need not be enabled
if (!pSession->ftHandoffInProgress) {
pSession->handoffRequested = VOS_TRUE;
}
// this session no longer needs UAPSD
pSession->apsdMask = 0;
// do any sessions still require UAPSD?
if(!pMac->psOffloadEnabled)
{
if (!sme_QosIsUapsdActive())
{
// No sessions require UAPSD so turn it off
// (really don't care when PMC stops it)
(void)pmcStopUapsd(pMac);
}
}
else
{
(void)pmcOffloadStopUapsd(pMac, sessionId);
}
pSession->uapsdAlreadyRequested = VOS_FALSE;
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessHandoffSuccessEv() - Function to process the
SME_QOS_CSR_HANDOFF_COMPLETE event indication from CSR
\param pEvent_info - Pointer to relevant info from CSR.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessHandoffSuccessEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
v_U8_t ac;
eHalStatus status = eHAL_STATUS_FAILURE;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d",
__func__, __LINE__,
sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
//go back to original state before handoff
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
switch(pACInfo->curr_state)
{
case SME_QOS_HANDOFF:
sme_QosStateTransition(sessionId, ac, pACInfo->prev_state);
//we will retry for the requested flow(s) with the new AP
if(SME_QOS_REQUESTED == pACInfo->curr_state)
{
pACInfo->curr_state = SME_QOS_LINK_UP;
}
status = eHAL_STATUS_SUCCESS;
break;
// FT logic, has already moved it to QOS_REQUESTED state during the
// reassoc request event, which would include the Qos (TSPEC) params
// in the reassoc req frame
case SME_QOS_REQUESTED:
break;
case SME_QOS_INIT:
case SME_QOS_CLOSED:
case SME_QOS_LINK_UP:
case SME_QOS_QOS_ON:
default:
#ifdef WLAN_FEATURE_VOWIFI_11R
/* In case of 11r - RIC, we request QoS and Hand-off at the same time hence the
state may be SME_QOS_REQUESTED */
if( pSession->ftHandoffInProgress )
break;
#endif
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d AC %d is in wrong state %d",
__func__, __LINE__,
sessionId, ac, pACInfo->curr_state);
VOS_ASSERT(0);
break;
}
}
return status;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessHandoffFailureEv() - Function to process the
SME_QOS_CSR_HANDOFF_FAILURE event indication from CSR
\param pEvent_info - Pointer to relevant info from CSR.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessHandoffFailureEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
v_U8_t ac;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d",
__func__, __LINE__,
sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
switch(pACInfo->curr_state)
{
case SME_QOS_HANDOFF:
sme_QosStateTransition(sessionId, ac, SME_QOS_INIT);
//need to clean up flows: TODO
vos_mem_zero(&pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0],
sizeof(sme_QosWmmTspecInfo));
vos_mem_zero(&pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0],
sizeof(sme_QosWmmTspecInfo));
vos_mem_zero(&pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_1],
sizeof(sme_QosWmmTspecInfo));
vos_mem_zero(&pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_1],
sizeof(sme_QosWmmTspecInfo));
pACInfo->tspec_mask_status = SME_QOS_TSPEC_MASK_CLEAR;
pACInfo->tspec_pending = 0;
pACInfo->num_flows[SME_QOS_TSPEC_INDEX_0] = 0;
pACInfo->num_flows[SME_QOS_TSPEC_INDEX_1] = 0;
break;
case SME_QOS_INIT:
case SME_QOS_CLOSED:
case SME_QOS_LINK_UP:
case SME_QOS_REQUESTED:
case SME_QOS_QOS_ON:
default:
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d AC %d is in wrong state %d",
__func__, __LINE__,
sessionId, ac, pACInfo->curr_state);
VOS_ASSERT(0);
break;
}
}
//no longer in handoff
pSession->handoffRequested = VOS_FALSE;
//clean up the assoc info
if(pSession->assocInfo.pBssDesc)
{
vos_mem_free(pSession->assocInfo.pBssDesc);
pSession->assocInfo.pBssDesc = NULL;
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessDisconnectEv() - Function to process the
SME_QOS_CSR_DISCONNECT_REQ or SME_QOS_CSR_DISCONNECT_IND event indication
from CSR
\param pEvent_info - Pointer to relevant info from CSR.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessDisconnectEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info)
{
sme_QosSessionInfo *pSession;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d",
__func__, __LINE__,
sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
if((pSession->handoffRequested)
#ifdef WLAN_FEATURE_VOWIFI_11R
/* In case of 11r - RIC, we request QoS and Hand-off at the same time hence the
state may be SME_QOS_REQUESTED */
&& !pSession->ftHandoffInProgress
#endif
)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: no need for state transition, should "
"already be in handoff state",
__func__, __LINE__);
if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[3].curr_state != SME_QOS_HANDOFF))
{
VOS_ASSERT(0);
return eHAL_STATUS_SUCCESS;
}
return eHAL_STATUS_SUCCESS;
}
sme_QosInitACs(pMac, sessionId);
// this session doesn't require UAPSD
pSession->apsdMask = 0;
if(!pMac->psOffloadEnabled)
{
// do any sessions still require UAPSD?
if (!sme_QosIsUapsdActive())
{
// No sessions require UAPSD so turn it off
// (really don't care when PMC stops it)
(void)pmcStopUapsd(pMac);
}
}
else
{
(void)pmcOffloadStopUapsd(pMac, sessionId);
}
pSession->uapsdAlreadyRequested = VOS_FALSE;
pSession->handoffRequested = VOS_FALSE;
pSession->readyForPowerSave = VOS_TRUE;
pSession->roamID = 0;
//need to clean up buffered req
sme_QosDeleteBufferedRequests(pMac, sessionId);
//need to clean up flows
sme_QosDeleteExistingFlows(pMac, sessionId);
//clean up the assoc info
if(pSession->assocInfo.pBssDesc)
{
vos_mem_free(pSession->assocInfo.pBssDesc);
pSession->assocInfo.pBssDesc = NULL;
}
sme_QosCb.sessionInfo[sessionId].sessionActive = VOS_FALSE;
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessJoinReqEv() - Function to process the
SME_QOS_CSR_JOIN_REQ event indication from CSR
\param pEvent_info - Pointer to relevant info from CSR.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessJoinReqEv(tpAniSirGlobal pMac, v_U8_t sessionId, void * pEvent_info)
{
sme_QosSessionInfo *pSession;
sme_QosEdcaAcType ac;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d",
__func__, __LINE__,
sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
if(pSession->handoffRequested)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: no need for state transition, should "
"already be in handoff state",
__func__, __LINE__);
if ((pSession->ac_info[0].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[1].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[2].curr_state != SME_QOS_HANDOFF) ||
(pSession->ac_info[3].curr_state != SME_QOS_HANDOFF))
{
// just print
VOS_ASSERT(0);
}
//buffer the existing flows to be renewed after handoff is done
sme_QosBufferExistingFlows(pMac, sessionId);
//clean up the control block partially for handoff
sme_QosCleanupCtrlBlkForHandoff(pMac, sessionId);
return eHAL_STATUS_SUCCESS;
}
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
sme_QosStateTransition(sessionId, ac, SME_QOS_INIT);
}
//clean up the assoc info if already set
if(pSession->assocInfo.pBssDesc)
{
vos_mem_free(pSession->assocInfo.pBssDesc);
pSession->assocInfo.pBssDesc = NULL;
}
return eHAL_STATUS_SUCCESS;
}
#ifdef WLAN_FEATURE_VOWIFI_11R
/*--------------------------------------------------------------------------
\brief sme_QosProcessPreauthSuccessInd() - Function to process the
SME_QOS_CSR_PREAUTH_SUCCESS_IND event indication from CSR
\param pEvent_info - Pointer to relevant info from CSR.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessPreauthSuccessInd(tpAniSirGlobal pMac,
v_U8_t sessionId, void * pEvent_info)
{
sme_QosSessionInfo *pSession;
tCsrRoamSession *pSmeSession = CSR_GET_SESSION( pMac, sessionId );
sme_QosACInfo *pACInfo;
v_U8_t ac;
eHalStatus status = eHAL_STATUS_SUCCESS;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
FL("invoked on SME session %d"), sessionId);
if (NULL == pSmeSession)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("pSmeSession is NULL"));
return eHAL_STATUS_INVALID_PARAMETER;
}
pSession = &sme_QosCb.sessionInfo[sessionId];
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
switch(pACInfo->curr_state)
{
case SME_QOS_LINK_UP:
case SME_QOS_REQUESTED:
case SME_QOS_QOS_ON:
sme_QosStateTransition(sessionId, ac, SME_QOS_HANDOFF);
break;
case SME_QOS_HANDOFF:
//print error msg
case SME_QOS_CLOSED:
case SME_QOS_INIT:
default:
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d AC %d is in wrong state %d",
__func__, __LINE__,
sessionId, ac, pACInfo->curr_state);
VOS_ASSERT(0);
break;
}
}
pSession->ftHandoffInProgress = VOS_TRUE;
// Check if its a 11R roaming before preparing the RIC IEs
if (csrRoamIs11rAssoc(pMac, sessionId)) {
v_U16_t ricOffset = 0;
v_U32_t ricIELength = 0;
v_U8_t *ricIE;
v_U8_t tspec_mask_status = 0;
v_U8_t tspec_pending_status = 0;
/* Data is accessed from saved PreAuth Rsp */
if (NULL == pSmeSession->ftSmeContext.psavedFTPreAuthRsp)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("psavedFTPreAuthRsp is NULL"));
return eHAL_STATUS_INVALID_PARAMETER;
}
/* Any Block Ack info there, should have been already filled by PE and
present in this buffer and the ric_ies_length should contain the
length of the whole RIC IEs. Filling of TSPEC info should start
from this length */
ricIE = pSmeSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies;
ricOffset =
pSmeSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies_length;
/* Now we have to process the currentTspeInfo inside this session and
create the RIC IEs */
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
volatile v_U8_t tspec_index = 0;
ricIELength = 0;
pACInfo = &pSession->ac_info[ac];
tspec_pending_status = pACInfo->tspec_pending;
tspec_mask_status = pACInfo->tspec_mask_status;
vos_mem_zero(pACInfo->ricIdentifier, SME_QOS_TSPEC_INDEX_MAX);
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
FL("AC %d ==> TSPEC status = %d, tspec pending = %d"),
ac, tspec_mask_status, tspec_pending_status);
do
{
if (tspec_mask_status & 0x1)
{
/* If a tspec status is pending, take requested_QoSInfo for
RIC request, else use curr_QoSInfo for the RIC request */
if (tspec_pending_status & 0x1)
{
status = sme_QosCreateTspecRICIE(pMac,
&pACInfo->requested_QoSInfo[tspec_index],
ricIE + ricOffset, &ricIELength,
&pACInfo->ricIdentifier[tspec_index]);
}
else
{
status = sme_QosCreateTspecRICIE(pMac,
&pACInfo->curr_QoSInfo[tspec_index],
ricIE + ricOffset, &ricIELength,
&pACInfo->ricIdentifier[tspec_index]);
}
}
ricOffset += ricIELength;
pSmeSession->ftSmeContext.psavedFTPreAuthRsp->ric_ies_length +=
ricIELength;
tspec_mask_status >>= 1;
tspec_pending_status >>= 1;
tspec_index++;
} while (tspec_mask_status);
}
}
return status;
}
#endif
/*--------------------------------------------------------------------------
\brief sme_QosProcessAddTsFailureRsp() - Function to process the
Addts request failure response came from PE
We will notify HDD only for the requested Flow, other Flows running on the AC
stay intact
\param pMac - Pointer to the global MAC parameter structure.
\param pRsp - Pointer to the addts response structure came from PE.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessAddTsFailureRsp(tpAniSirGlobal pMac,
v_U8_t sessionId,
tSirAddtsRspInfo * pRsp)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosEdcaAcType ac;
sme_QosSearchInfo search_key;
v_U8_t tspec_pending;
sme_QosWmmUpType up = (sme_QosWmmUpType)pRsp->tspec.tsinfo.traffic.userPrio;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d for UP %d",
__func__, __LINE__,
sessionId, up);
ac = sme_QosUpToAc(up);
if(SME_QOS_EDCA_AC_MAX == ac)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: invalid AC %d from UP %d",
__func__, __LINE__, ac, up);
return eHAL_STATUS_FAILURE;
}
pSession = &sme_QosCb.sessionInfo[sessionId];
pACInfo = &pSession->ac_info[ac];
// is there a TSPEC request pending on this AC?
tspec_pending = pACInfo->tspec_pending;
if(!tspec_pending)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d an AddTS is not pending on AC %d",
__func__, __LINE__,
sessionId, ac);
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo));
//set the key type & the key to be searched in the Flow List
search_key.key.ac_type = ac;
search_key.index = SME_QOS_SEARCH_KEY_INDEX_2;
search_key.sessionId = sessionId;
if(!HAL_STATUS_SUCCESS(sme_QosFindAllInFlowList(pMac, search_key, sme_QosAddTsFailureFnp)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d no match found for ac = %d",
__func__, __LINE__,
sessionId, search_key.key.ac_type);
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
vos_mem_zero(&pACInfo->requested_QoSInfo[tspec_pending - 1],
sizeof(sme_QosWmmTspecInfo));
if((!pACInfo->num_flows[0])&&
(!pACInfo->num_flows[1]))
{
pACInfo->tspec_mask_status &= SME_QOS_TSPEC_MASK_BIT_1_2_SET &
(~pACInfo->tspec_pending);
sme_QosStateTransition(sessionId, ac, SME_QOS_LINK_UP);
}
else
{
sme_QosStateTransition(sessionId, ac, SME_QOS_QOS_ON);
}
pACInfo->tspec_pending = 0;
(void)sme_QosProcessBufferedCmd(sessionId);
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosUpdateTspecMask() - Utility function to update the tspec.
Typical usage while aggregating unidirectional flows into a bi-directional
flow on AC which is running multiple flows
\param sessionId - Session upon which the TSPEC is being updated
\param ac - Enumeration of the various EDCA Access Categories.
\param old_tspec_mask - on which tspec per AC, the update is requested
\param new_tspec_mask - tspec to be set for this AC
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
static eHalStatus sme_QosUpdateTspecMask(v_U8_t sessionId,
sme_QosSearchInfo search_key,
v_U8_t new_tspec_mask)
{
tListElem *pEntry= NULL, *pNextEntry = NULL;
sme_QosFlowInfoEntry *flow_info = NULL;
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d for AC %d TSPEC %d",
__func__, __LINE__,
sessionId, search_key.key.ac_type, new_tspec_mask);
pSession = &sme_QosCb.sessionInfo[sessionId];
if (search_key.key.ac_type < SME_QOS_EDCA_AC_MAX)
{
pACInfo = &pSession->ac_info[search_key.key.ac_type];
}
else
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Exceeded the array bounds of pSession->ac_info",
__func__, __LINE__);
VOS_ASSERT (0);
return eHAL_STATUS_FAILURE;
}
pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE );
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Flow List empty, nothing to update",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
while( pEntry )
{
pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE );
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
if(search_key.sessionId == flow_info->sessionId)
{
if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_4)
{
if((search_key.key.ac_type == flow_info->ac_type) &&
(search_key.direction == flow_info->QoSInfo.ts_info.direction))
{
//msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Flow %d matches",
__func__, __LINE__,
flow_info->QosFlowID);
pACInfo->num_flows[flow_info->tspec_mask - 1]--;
pACInfo->num_flows[new_tspec_mask - 1]++;
flow_info->tspec_mask = new_tspec_mask;
}
}
else if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_5)
{
if((search_key.key.ac_type == flow_info->ac_type) &&
(search_key.tspec_mask == flow_info->tspec_mask))
{
//msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Flow %d matches",
__func__, __LINE__,
flow_info->QosFlowID);
pACInfo->num_flows[flow_info->tspec_mask - 1]--;
pACInfo->num_flows[new_tspec_mask - 1]++;
flow_info->tspec_mask = new_tspec_mask;
}
}
}
pEntry = pNextEntry;
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessAddTsSuccessRsp() - Function to process the
Addts request success response came from PE
We will notify HDD with addts success for the requested Flow, & for other
Flows running on the AC we will send an addts modify status
\param pMac - Pointer to the global MAC parameter structure.
\param pRsp - Pointer to the addts response structure came from PE.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessAddTsSuccessRsp(tpAniSirGlobal pMac,
v_U8_t sessionId,
tSirAddtsRspInfo * pRsp)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosEdcaAcType ac, ac_index;
sme_QosSearchInfo search_key;
sme_QosSearchInfo search_key1;
v_U8_t tspec_pending;
tListElem *pEntry= NULL;
sme_QosFlowInfoEntry *flow_info = NULL;
sme_QosWmmUpType up = (sme_QosWmmUpType)pRsp->tspec.tsinfo.traffic.userPrio;
#ifdef FEATURE_WLAN_DIAG_SUPPORT
WLAN_VOS_DIAG_EVENT_DEF(qos, vos_event_wlan_qos_payload_type);
vos_log_qos_tspec_pkt_type *log_ptr = NULL;
#endif //FEATURE_WLAN_DIAG_SUPPORT
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d for UP %d",
__func__, __LINE__,
sessionId, up);
pSession = &sme_QosCb.sessionInfo[sessionId];
ac = sme_QosUpToAc(up);
if(SME_QOS_EDCA_AC_MAX == ac)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: invalid AC %d from UP %d",
__func__, __LINE__, ac, up);
return eHAL_STATUS_FAILURE;
}
pACInfo = &pSession->ac_info[ac];
// is there a TSPEC request pending on this AC?
tspec_pending = pACInfo->tspec_pending;
if(!tspec_pending)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d an AddTS is not pending on AC %d",
__func__, __LINE__,
sessionId, ac);
return eHAL_STATUS_FAILURE;
}
//App is looking for APSD or the App which was looking for APSD has been
//released, so STA re-negotiated with AP
if(pACInfo->requested_QoSInfo[tspec_pending - 1].ts_info.psb)
{
//update the session's apsd mask
pSession->apsdMask |= 1 << (SME_QOS_EDCA_AC_VO - ac);
}
else
{
if(((SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~tspec_pending) > 0) &&
((SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~tspec_pending) <=
SME_QOS_TSPEC_INDEX_MAX))
{
if(!pACInfo->requested_QoSInfo
[(SME_QOS_TSPEC_MASK_BIT_1_2_SET & ~tspec_pending) - 1].ts_info.psb)
{
//update the session's apsd mask
pSession->apsdMask &= ~(1 << (SME_QOS_EDCA_AC_VO - ac));
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Exceeded the array bounds of pACInfo->requested_QosInfo",
__func__, __LINE__);
VOS_ASSERT (0);
return eHAL_STATUS_FAILURE;
}
}
pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.burst_size_defn =
pRsp->tspec.tsinfo.traffic.burstSizeDefn;
pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.ack_policy =
pRsp->tspec.tsinfo.traffic.ackPolicy;
pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.up =
pRsp->tspec.tsinfo.traffic.userPrio;
pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.psb =
pRsp->tspec.tsinfo.traffic.psb;
pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.direction =
pRsp->tspec.tsinfo.traffic.direction;
pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.tid =
pRsp->tspec.tsinfo.traffic.tsid;
pACInfo->curr_QoSInfo[tspec_pending - 1].nominal_msdu_size =
pRsp->tspec.nomMsduSz;
pACInfo->curr_QoSInfo[tspec_pending - 1].maximum_msdu_size =
pRsp->tspec.maxMsduSz;
pACInfo->curr_QoSInfo[tspec_pending - 1].min_service_interval =
pRsp->tspec.minSvcInterval;
pACInfo->curr_QoSInfo[tspec_pending - 1].max_service_interval =
pRsp->tspec.maxSvcInterval;
pACInfo->curr_QoSInfo[tspec_pending - 1].inactivity_interval =
pRsp->tspec.inactInterval;
pACInfo->curr_QoSInfo[tspec_pending - 1].suspension_interval =
pRsp->tspec.suspendInterval;
pACInfo->curr_QoSInfo[tspec_pending - 1].svc_start_time =
pRsp->tspec.svcStartTime;
pACInfo->curr_QoSInfo[tspec_pending - 1].min_data_rate =
pRsp->tspec.minDataRate;
pACInfo->curr_QoSInfo[tspec_pending - 1].mean_data_rate =
pRsp->tspec.meanDataRate;
pACInfo->curr_QoSInfo[tspec_pending - 1].peak_data_rate =
pRsp->tspec.peakDataRate;
pACInfo->curr_QoSInfo[tspec_pending - 1].max_burst_size =
pRsp->tspec.maxBurstSz;
pACInfo->curr_QoSInfo[tspec_pending - 1].delay_bound =
pRsp->tspec.delayBound;
pACInfo->curr_QoSInfo[tspec_pending - 1].min_phy_rate =
pRsp->tspec.minPhyRate;
pACInfo->curr_QoSInfo[tspec_pending - 1].surplus_bw_allowance =
pRsp->tspec.surplusBw;
pACInfo->curr_QoSInfo[tspec_pending - 1].medium_time =
pRsp->tspec.mediumTime;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: On session %d AddTspec Medium Time %d",
__func__, __LINE__,
sessionId, pRsp->tspec.mediumTime);
/* Check if the current flow is for bi-directional. If so, update the number of flows
* to reflect that all flows are aggregated into tspec index 0. */
if((pACInfo->curr_QoSInfo[pACInfo->tspec_pending - 1].ts_info.direction == SME_QOS_WMM_TS_DIR_BOTH) &&
(pACInfo->num_flows[SME_QOS_TSPEC_INDEX_1] > 0))
{
vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo));
/* update tspec_mask for all the flows having SME_QOS_TSPEC_MASK_BIT_2_SET to SME_QOS_TSPEC_MASK_BIT_1_SET */
search_key.key.ac_type = ac;
search_key.index = SME_QOS_SEARCH_KEY_INDEX_5;
search_key.sessionId = sessionId;
search_key.tspec_mask = SME_QOS_TSPEC_MASK_BIT_2_SET;
sme_QosUpdateTspecMask(sessionId, search_key, SME_QOS_TSPEC_MASK_BIT_1_SET);
}
vos_mem_zero(&search_key1, sizeof(sme_QosSearchInfo));
//set the horenewal field in control block if needed
search_key1.index = SME_QOS_SEARCH_KEY_INDEX_3;
search_key1.key.reason = SME_QOS_REASON_SETUP;
search_key1.sessionId = sessionId;
for(ac_index = SME_QOS_EDCA_AC_BE; ac_index < SME_QOS_EDCA_AC_MAX; ac_index++)
{
pEntry = sme_QosFindInFlowList(search_key1);
if(pEntry)
{
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
if(flow_info->ac_type == ac)
{
pACInfo->hoRenewal = flow_info->hoRenewal;
break;
}
}
}
vos_mem_zero(&search_key, sizeof(sme_QosSearchInfo));
//set the key type & the key to be searched in the Flow List
search_key.key.ac_type = ac;
search_key.index = SME_QOS_SEARCH_KEY_INDEX_2;
search_key.sessionId = sessionId;
//notify HDD the success for the requested flow
//notify all the other flows running on the AC that QoS got modified
if(!HAL_STATUS_SUCCESS(sme_QosFindAllInFlowList(pMac, search_key, sme_QosAddTsSuccessFnp)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d no match found for ac %d",
__func__, __LINE__,
sessionId, search_key.key.ac_type);
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
pACInfo->hoRenewal = VOS_FALSE;
vos_mem_zero(&pACInfo->requested_QoSInfo[tspec_pending - 1],
sizeof(sme_QosWmmTspecInfo));
//event: EVENT_WLAN_QOS
#ifdef FEATURE_WLAN_DIAG_SUPPORT
qos.eventId = SME_QOS_DIAG_ADDTS_RSP;
qos.reasonCode = SME_QOS_DIAG_ADDTS_ADMISSION_ACCEPTED;
WLAN_VOS_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS);
WLAN_VOS_DIAG_LOG_ALLOC(log_ptr, vos_log_qos_tspec_pkt_type, LOG_WLAN_QOS_TSPEC_C);
if(log_ptr)
{
log_ptr->delay_bound = pACInfo->curr_QoSInfo[tspec_pending - 1].delay_bound;
log_ptr->inactivity_interval = pACInfo->curr_QoSInfo[tspec_pending - 1].inactivity_interval;
log_ptr->max_burst_size = pACInfo->curr_QoSInfo[tspec_pending - 1].max_burst_size;
log_ptr->max_service_interval = pACInfo->curr_QoSInfo[tspec_pending - 1].max_service_interval;
log_ptr->maximum_msdu_size = pACInfo->curr_QoSInfo[tspec_pending - 1].maximum_msdu_size;
log_ptr->mean_data_rate = pACInfo->curr_QoSInfo[tspec_pending - 1].mean_data_rate;
log_ptr->medium_time = pACInfo->curr_QoSInfo[tspec_pending - 1].medium_time;
log_ptr->min_data_rate = pACInfo->curr_QoSInfo[tspec_pending - 1].min_data_rate;
log_ptr->min_phy_rate = pACInfo->curr_QoSInfo[tspec_pending - 1].min_phy_rate;
log_ptr->min_service_interval = pACInfo->curr_QoSInfo[tspec_pending - 1].min_service_interval;
log_ptr->nominal_msdu_size = pACInfo->curr_QoSInfo[tspec_pending - 1].nominal_msdu_size;
log_ptr->peak_data_rate = pACInfo->curr_QoSInfo[tspec_pending - 1].peak_data_rate;
log_ptr->surplus_bw_allowance = pACInfo->curr_QoSInfo[tspec_pending - 1].surplus_bw_allowance;
log_ptr->suspension_interval = pACInfo->curr_QoSInfo[tspec_pending - 1].suspension_interval;
log_ptr->svc_start_time = pACInfo->curr_QoSInfo[tspec_pending - 1].svc_start_time;
log_ptr->tsinfo[0] = pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.direction << 5 |
pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.tid << 1;
log_ptr->tsinfo[1] = pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.up << 11 |
pACInfo->curr_QoSInfo[tspec_pending - 1].ts_info.psb << 10;
log_ptr->tsinfo[2] = 0;
}
WLAN_VOS_DIAG_LOG_REPORT(log_ptr);
#endif //FEATURE_WLAN_DIAG_SUPPORT
#if defined(FEATURE_WLAN_ESE) && !defined(FEATURE_WLAN_ESE_UPLOAD)
if (ac == SME_QOS_EDCA_AC_VO)
{
// Indicate to neighbor roam logic of the new required VO
// ac bandwidth requirement.
csrNeighborRoamIndicateVoiceBW( pMac, pACInfo->curr_QoSInfo[tspec_pending - 1].peak_data_rate, TRUE );
}
#endif
pACInfo->tspec_pending = 0;
sme_QosStateTransition(sessionId, ac, SME_QOS_QOS_ON);
(void)sme_QosProcessBufferedCmd(sessionId);
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosAggregateParams() - Utility function to increment the TSPEC
params per AC. Typical usage while using flow aggregation or deletion of flows
\param pInput_Tspec_Info - Pointer to sme_QosWmmTspecInfo which contains the
WMM TSPEC related info with which pCurrent_Tspec_Info will be updated
\param pCurrent_Tspec_Info - Pointer to sme_QosWmmTspecInfo which contains
current the WMM TSPEC related info
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosAggregateParams(
sme_QosWmmTspecInfo * pInput_Tspec_Info,
sme_QosWmmTspecInfo * pCurrent_Tspec_Info,
sme_QosWmmTspecInfo * pUpdated_Tspec_Info)
{
sme_QosWmmTspecInfo TspecInfo;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked",
__func__, __LINE__);
if(!pInput_Tspec_Info)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: input is NULL, nothing to aggregate",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
if(!pCurrent_Tspec_Info)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Current is NULL, can't aggregate",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
vos_mem_copy(&TspecInfo, pCurrent_Tspec_Info,
sizeof(sme_QosWmmTspecInfo));
TspecInfo.ts_info.psb = pInput_Tspec_Info->ts_info.psb;
/*-------------------------------------------------------------------------
APSD preference is only meaningful if service interval was set by app
-------------------------------------------------------------------------*/
if(pCurrent_Tspec_Info->min_service_interval &&
pInput_Tspec_Info->min_service_interval &&
(pCurrent_Tspec_Info->ts_info.direction !=
pInput_Tspec_Info->ts_info.direction))
{
TspecInfo.min_service_interval = VOS_MIN(
pCurrent_Tspec_Info->min_service_interval,
pInput_Tspec_Info->min_service_interval);
}
else if(pInput_Tspec_Info->min_service_interval)
{
TspecInfo.min_service_interval = pInput_Tspec_Info->min_service_interval;
}
if(pCurrent_Tspec_Info->max_service_interval &&
pInput_Tspec_Info->max_service_interval &&
(pCurrent_Tspec_Info->ts_info.direction !=
pInput_Tspec_Info->ts_info.direction))
{
TspecInfo.max_service_interval = VOS_MIN(
pCurrent_Tspec_Info->max_service_interval,
pInput_Tspec_Info->max_service_interval);
}
else
{
TspecInfo.max_service_interval = pInput_Tspec_Info->max_service_interval;
}
/*-------------------------------------------------------------------------
If directions don't match, it must necessarily be both uplink and
downlink
-------------------------------------------------------------------------*/
if(pCurrent_Tspec_Info->ts_info.direction !=
pInput_Tspec_Info->ts_info.direction)
{
TspecInfo.ts_info.direction = pInput_Tspec_Info->ts_info.direction;
}
/*-------------------------------------------------------------------------
Max MSDU size : these sizes are `maxed'
-------------------------------------------------------------------------*/
TspecInfo.maximum_msdu_size = VOS_MAX(pCurrent_Tspec_Info->maximum_msdu_size,
pInput_Tspec_Info->maximum_msdu_size);
/*-------------------------------------------------------------------------
Inactivity interval : these sizes are `maxed'
-------------------------------------------------------------------------*/
TspecInfo.inactivity_interval = VOS_MAX(pCurrent_Tspec_Info->inactivity_interval,
pInput_Tspec_Info->inactivity_interval);
/*-------------------------------------------------------------------------
Delay bounds: min of all values
Check on 0: if 0, it means initial value since delay can never be 0!!
-------------------------------------------------------------------------*/
if(pCurrent_Tspec_Info->delay_bound)
{
TspecInfo.delay_bound = VOS_MIN(pCurrent_Tspec_Info->delay_bound,
pInput_Tspec_Info->delay_bound);
}
else
{
TspecInfo.delay_bound = pInput_Tspec_Info->delay_bound;
}
TspecInfo.max_burst_size = VOS_MAX(pCurrent_Tspec_Info->max_burst_size,
pInput_Tspec_Info->max_burst_size);
/*-------------------------------------------------------------------------
Nominal MSDU size also has a fixed bit that needs to be `handled' before
aggregation
This can be handled only if previous size is the same as new or both have
the fixed bit set
These sizes are not added: but `maxed'
-------------------------------------------------------------------------*/
TspecInfo.nominal_msdu_size = VOS_MAX(
pCurrent_Tspec_Info->nominal_msdu_size & ~SME_QOS_16BIT_MSB,
pInput_Tspec_Info->nominal_msdu_size & ~SME_QOS_16BIT_MSB);
if( ((pCurrent_Tspec_Info->nominal_msdu_size == 0) ||
(pCurrent_Tspec_Info->nominal_msdu_size & SME_QOS_16BIT_MSB)) &&
((pInput_Tspec_Info->nominal_msdu_size == 0) ||
(pInput_Tspec_Info->nominal_msdu_size & SME_QOS_16BIT_MSB)))
{
TspecInfo.nominal_msdu_size |= SME_QOS_16BIT_MSB;
}
/*-------------------------------------------------------------------------
Data rates:
Add up the rates for aggregation
-------------------------------------------------------------------------*/
SME_QOS_BOUNDED_U32_ADD_Y_TO_X( TspecInfo.peak_data_rate,
pInput_Tspec_Info->peak_data_rate );
SME_QOS_BOUNDED_U32_ADD_Y_TO_X( TspecInfo.min_data_rate,
pInput_Tspec_Info->min_data_rate );
/* mean data rate = peak data rate: aggregate to be flexible on apps */
SME_QOS_BOUNDED_U32_ADD_Y_TO_X( TspecInfo.mean_data_rate,
pInput_Tspec_Info->mean_data_rate );
/*-------------------------------------------------------------------------
Suspension interval : this is set to the inactivity interval since per
spec it is less than or equal to inactivity interval
This is not provided by app since we currently don't support the HCCA
mode of operation
Currently set it to 0 to avoid confusion: Cisco ESE needs ~0; spec
requires inactivity interval to be > suspension interval: this could
be tricky!
-------------------------------------------------------------------------*/
TspecInfo.suspension_interval = pInput_Tspec_Info->suspension_interval;
/*-------------------------------------------------------------------------
Remaining parameters do not come from app as they are very WLAN
air interface specific
Set meaningful values here
-------------------------------------------------------------------------*/
TspecInfo.medium_time = 0; /* per WMM spec */
TspecInfo.min_phy_rate = SME_QOS_MIN_PHY_RATE;
TspecInfo.svc_start_time = 0; /* arbitrary */
TspecInfo.surplus_bw_allowance += pInput_Tspec_Info->surplus_bw_allowance;
if(TspecInfo.surplus_bw_allowance > SME_QOS_SURPLUS_BW_ALLOWANCE)
{
TspecInfo.surplus_bw_allowance = SME_QOS_SURPLUS_BW_ALLOWANCE;
}
/* Set ack_policy to block ack even if one stream requests block ack policy */
if((pInput_Tspec_Info->ts_info.ack_policy == SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK) ||
(pCurrent_Tspec_Info->ts_info.ack_policy == SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK))
{
TspecInfo.ts_info.ack_policy = SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK;
}
if(pInput_Tspec_Info->ts_info.burst_size_defn || pCurrent_Tspec_Info->ts_info.burst_size_defn )
{
TspecInfo.ts_info.burst_size_defn = 1;
}
if(pUpdated_Tspec_Info)
{
vos_mem_copy(pUpdated_Tspec_Info, &TspecInfo,
sizeof(sme_QosWmmTspecInfo));
}
else
{
vos_mem_copy(pCurrent_Tspec_Info, &TspecInfo,
sizeof(sme_QosWmmTspecInfo));
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosUpdateParams() - Utility function to update the TSPEC
params per AC. Typical usage while deleting flows on AC which is running
multiple flows
\param sessionId - Session upon which the TSPEC is being updated
\param ac - Enumeration of the various EDCA Access Categories.
\param tspec_mask - on which tspec per AC, the update is requested
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
static eHalStatus sme_QosUpdateParams(v_U8_t sessionId,
sme_QosEdcaAcType ac,
v_U8_t tspec_mask,
sme_QosWmmTspecInfo * pTspec_Info)
{
tListElem *pEntry= NULL, *pNextEntry = NULL;
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosFlowInfoEntry *flow_info = NULL;
sme_QosWmmTspecInfo Tspec_Info;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: invoked on session %d for AC %d TSPEC %d",
__func__, __LINE__,
sessionId, ac, tspec_mask);
if(!pTspec_Info)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: output is NULL, can't aggregate",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
vos_mem_zero(&Tspec_Info, sizeof(sme_QosWmmTspecInfo));
pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE );
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Flow List empty, nothing to update",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
pSession = &sme_QosCb.sessionInfo[sessionId];
pACInfo = &pSession->ac_info[ac];
//init the TS info field
Tspec_Info.ts_info.up = pACInfo->curr_QoSInfo[tspec_mask - 1].ts_info.up;
Tspec_Info.ts_info.psb = pACInfo->curr_QoSInfo[tspec_mask - 1].ts_info.psb;
Tspec_Info.ts_info.tid = pACInfo->curr_QoSInfo[tspec_mask - 1].ts_info.tid;
while( pEntry )
{
pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE );
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
if((sessionId == flow_info->sessionId) &&
(ac == flow_info->ac_type) &&
(tspec_mask == flow_info->tspec_mask))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Flow %d matches",
__func__, __LINE__,
flow_info->QosFlowID);
if((SME_QOS_REASON_RELEASE == flow_info->reason ) ||
(SME_QOS_REASON_MODIFY == flow_info->reason))
{
//msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Skipping Flow %d as it is marked "
"for release/modify",
__func__, __LINE__,
flow_info->QosFlowID);
}
else if(!HAL_STATUS_SUCCESS(sme_QosAggregateParams(&flow_info->QoSInfo,
&Tspec_Info,
NULL)))
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: sme_QosAggregateParams() failed",
__func__, __LINE__);
}
}
pEntry = pNextEntry;
}
// return the aggregate
*pTspec_Info = Tspec_Info;
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosAcToUp() - Utility function to map an AC to UP
Note: there is a quantization loss here because 4 ACs are mapped to 8 UPs
Mapping is done for consistency
\param ac - Enumeration of the various EDCA Access Categories.
\return an User Priority
\sa
--------------------------------------------------------------------------*/
sme_QosWmmUpType sme_QosAcToUp(sme_QosEdcaAcType ac)
{
sme_QosWmmUpType up = SME_QOS_WMM_UP_MAX;
if(ac >= 0 && ac < SME_QOS_EDCA_AC_MAX)
{
up = sme_QosACtoUPMap[ac];
}
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED,
"%s: %d: ac = %d up = %d returned",
__func__, __LINE__, ac, up);
return up;
}
/*--------------------------------------------------------------------------
\brief sme_QosUpToAc() - Utility function to map an UP to AC
\param up - Enumeration of the various User priorities (UP).
\return an Access Category
\sa
--------------------------------------------------------------------------*/
sme_QosEdcaAcType sme_QosUpToAc(sme_QosWmmUpType up)
{
sme_QosEdcaAcType ac = SME_QOS_EDCA_AC_MAX;
if(up >= 0 && up < SME_QOS_WMM_UP_MAX)
{
ac = sme_QosUPtoACMap[up];
}
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED,
"%s: %d: up = %d ac = %d returned",
__func__, __LINE__, up, ac);
return ac;
}
/*--------------------------------------------------------------------------
\brief sme_QosStateTransition() - The state transition function per AC. We
save the previous state also.
\param sessionId - Session upon which the state machine is running
\param ac - Enumeration of the various EDCA Access Categories.
\param new_state - The state FSM is moving to.
\return None
\sa
--------------------------------------------------------------------------*/
static void sme_QosStateTransition(v_U8_t sessionId,
sme_QosEdcaAcType ac,
sme_QosStates new_state)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
pSession = &sme_QosCb.sessionInfo[sessionId];
pACInfo = &pSession->ac_info[ac];
pACInfo->prev_state = pACInfo->curr_state;
pACInfo->curr_state = new_state;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: On session %d new state=%d, old state=%d, for AC=%d",
__func__, __LINE__,
sessionId, pACInfo->curr_state, pACInfo->prev_state, ac );
}
/*--------------------------------------------------------------------------
\brief sme_QosFindInFlowList() - Utility function to find an flow entry from
the flow_list.
\param search_key - We can either use the flowID or the ac type to find the
entry in the flow list.
A bitmap in sme_QosSearchInfo tells which key to use. Starting from LSB,
bit 0 - Flow ID
bit 1 - AC type
\return the pointer to the entry in the link list
\sa
--------------------------------------------------------------------------*/
tListElem *sme_QosFindInFlowList(sme_QosSearchInfo search_key)
{
tListElem *pEntry= NULL, *pNextEntry = NULL;
sme_QosFlowInfoEntry *flow_info = NULL;
pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE );
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Flow List empty, can't search",
__func__, __LINE__);
return NULL;
}
while( pEntry )
{
pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE );
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
if((search_key.sessionId == flow_info->sessionId) ||
(search_key.sessionId == SME_QOS_SEARCH_SESSION_ID_ANY))
{
if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_1)
{
if(search_key.key.QosFlowID == flow_info->QosFlowID)
{
//msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: match found on flowID, ending search",
__func__, __LINE__);
break;
}
}
else if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_2)
{
if(search_key.key.ac_type == flow_info->ac_type)
{
//msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: match found on ac, ending search",
__func__, __LINE__);
break;
}
}
else if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_3)
{
if(search_key.key.reason == flow_info->reason)
{
//msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: match found on reason, ending search",
__func__, __LINE__);
break;
}
}
else if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_4)
{
if((search_key.key.ac_type == flow_info->ac_type) &&
(search_key.direction == flow_info->QoSInfo.ts_info.direction))
{
//msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: match found on reason, ending search",
__func__, __LINE__);
break;
}
}
}
pEntry = pNextEntry;
}
return pEntry;
}
/*--------------------------------------------------------------------------
\brief sme_QosFindAllInFlowList() - Utility function to find an flow entry
from the flow_list & act on it.
\param search_key - We can either use the flowID or the ac type to find the
entry in the flow list.
A bitmap in sme_QosSearchInfo tells which key to use. Starting from LSB,
bit 0 - Flow ID
bit 1 - AC type
\param fnp - function pointer specifying the action type for the entry found
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosFindAllInFlowList(tpAniSirGlobal pMac,
sme_QosSearchInfo search_key,
sme_QosProcessSearchEntry fnp)
{
tListElem *pEntry= NULL, *pNextEntry = NULL;
sme_QosSessionInfo *pSession;
sme_QosFlowInfoEntry *flow_info = NULL;
eHalStatus status = eHAL_STATUS_FAILURE;
pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE );
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Flow List empty, can't search",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
while( pEntry )
{
pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE );
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
pSession = &sme_QosCb.sessionInfo[flow_info->sessionId];
if((search_key.sessionId == flow_info->sessionId) ||
(search_key.sessionId == SME_QOS_SEARCH_SESSION_ID_ANY))
{
if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_1)
{
if(search_key.key.QosFlowID == flow_info->QosFlowID)
{
//msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: match found on flowID, ending search",
__func__, __LINE__);
status = fnp(pMac, pEntry);
if(eHAL_STATUS_FAILURE == status)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Failed to process entry",
__func__, __LINE__);
break;
}
}
}
else if(search_key.index & SME_QOS_SEARCH_KEY_INDEX_2)
{
if(search_key.key.ac_type == flow_info->ac_type)
{
//msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: match found on ac, ending search",
__func__, __LINE__);
flow_info->hoRenewal = pSession->ac_info[flow_info->ac_type].hoRenewal;
status = fnp(pMac, pEntry);
if(eHAL_STATUS_FAILURE == status)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Failed to process entry",
__func__, __LINE__);
break;
}
}
}
}
pEntry = pNextEntry;
}
return status;
}
/*--------------------------------------------------------------------------
\brief sme_QosIsACM() - Utility function to check if a particular AC
mandates Admission Control.
\param ac - Enumeration of the various EDCA Access Categories.
\return VOS_TRUE if the AC mandates Admission Control
\sa
--------------------------------------------------------------------------*/
v_BOOL_t sme_QosIsACM(tpAniSirGlobal pMac, tSirBssDescription *pSirBssDesc,
sme_QosEdcaAcType ac, tDot11fBeaconIEs *pIes)
{
v_BOOL_t ret_val = VOS_FALSE;
tDot11fBeaconIEs *pIesLocal;
if(!pSirBssDesc)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: pSirBssDesc is NULL",
__func__, __LINE__);
return VOS_FALSE;
}
if (NULL != pIes)
{
/* IEs were provided so use them locally */
pIesLocal = pIes;
}
else
{
/* IEs were not provided so parse them ourselves */
if (!HAL_STATUS_SUCCESS(csrGetParsedBssDescriptionIEs(pMac, pSirBssDesc, &pIesLocal)))
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: csrGetParsedBssDescriptionIEs() failed",
__func__, __LINE__);
return VOS_FALSE;
}
/* if success then pIesLocal was allocated */
}
if(CSR_IS_QOS_BSS(pIesLocal))
{
switch(ac)
{
case SME_QOS_EDCA_AC_BE:
if(pIesLocal->WMMParams.acbe_acm) ret_val = VOS_TRUE;
break;
case SME_QOS_EDCA_AC_BK:
if(pIesLocal->WMMParams.acbk_acm) ret_val = VOS_TRUE;
break;
case SME_QOS_EDCA_AC_VI:
if(pIesLocal->WMMParams.acvi_acm) ret_val = VOS_TRUE;
break;
case SME_QOS_EDCA_AC_VO:
if(pIesLocal->WMMParams.acvo_acm) ret_val = VOS_TRUE;
break;
default:
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: unknown AC = %d",
__func__, __LINE__, ac);
//Assert
VOS_ASSERT(0);
break;
}
}//IS_QOS_BSS
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: ACM = %d for AC = %d",
__func__, __LINE__, ret_val, ac );
if (NULL == pIes)
{
/* IEs were allocated locally so free them */
vos_mem_free(pIesLocal);
}
return ret_val;
}
/*--------------------------------------------------------------------------
\brief sme_QosBufferExistingFlows() - Utility function to buffer the existing
flows in flow_list, so that we can renew them after handoff is done.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
static eHalStatus sme_QosBufferExistingFlows(tpAniSirGlobal pMac,
v_U8_t sessionId)
{
tListElem *pEntry= NULL, *pNextEntry = NULL;
sme_QosSessionInfo *pSession;
sme_QosFlowInfoEntry *flow_info = NULL;
sme_QosCmdInfo cmd;
pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE );
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Flow List empty, nothing to buffer",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
while( pEntry )
{
pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE );
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
if (flow_info->sessionId == sessionId)
{
if((SME_QOS_REASON_REQ_SUCCESS == flow_info->reason )||
(SME_QOS_REASON_SETUP == flow_info->reason ))
{
cmd.command = SME_QOS_SETUP_REQ;
cmd.pMac = pMac;
cmd.sessionId = sessionId;
cmd.u.setupCmdInfo.HDDcontext = flow_info->HDDcontext;
cmd.u.setupCmdInfo.QoSInfo = flow_info->QoSInfo;
cmd.u.setupCmdInfo.QoSCallback = flow_info->QoSCallback;
cmd.u.setupCmdInfo.UPType = SME_QOS_WMM_UP_MAX;//shouldn't be needed
cmd.u.setupCmdInfo.QosFlowID = flow_info->QosFlowID;
if(SME_QOS_REASON_SETUP == flow_info->reason )
{
cmd.u.setupCmdInfo.hoRenewal = VOS_FALSE;
}
else
{
cmd.u.setupCmdInfo.hoRenewal = VOS_TRUE;//TODO: might need this for modify
}
if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_TRUE)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: couldn't buffer the setup request for "
"flow %d in handoff state",
__func__, __LINE__,
flow_info->QosFlowID);
}
else
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: buffered a setup request for "
"flow %d in handoff state",
__func__, __LINE__,
flow_info->QosFlowID);
}
}
else if(SME_QOS_REASON_RELEASE == flow_info->reason )
{
cmd.command = SME_QOS_RELEASE_REQ;
cmd.pMac = pMac;
cmd.sessionId = sessionId;
cmd.u.releaseCmdInfo.QosFlowID = flow_info->QosFlowID;
if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_TRUE)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: couldn't buffer the release request for "
"flow %d in handoff state",
__func__, __LINE__,
flow_info->QosFlowID);
}
else
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: buffered a release request for "
"flow %d in handoff state",
__func__, __LINE__,
flow_info->QosFlowID);
}
}
else if(SME_QOS_REASON_MODIFY_PENDING == flow_info->reason)
{
cmd.command = SME_QOS_MODIFY_REQ;
cmd.pMac = pMac;
cmd.sessionId = sessionId;
cmd.u.modifyCmdInfo.QosFlowID = flow_info->QosFlowID;
cmd.u.modifyCmdInfo.QoSInfo = flow_info->QoSInfo;
if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_TRUE)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: couldn't buffer the modify request for "
"flow %d in handoff state",
__func__, __LINE__,
flow_info->QosFlowID);
}
else
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: buffered a modify request for "
"flow %d in handoff state",
__func__, __LINE__,
flow_info->QosFlowID);
}
}
//delete the entry from Flow List
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Deleting original entry at %pK with flowID %d",
__func__, __LINE__,
flow_info, flow_info->QosFlowID);
csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE );
vos_mem_free(flow_info);
}
pEntry = pNextEntry;
}
pSession = &sme_QosCb.sessionInfo[sessionId];
pSession->uapsdAlreadyRequested = VOS_FALSE;
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosDeleteExistingFlows() - Utility function to Delete the existing
flows in flow_list, if we lost connectivity.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
static eHalStatus sme_QosDeleteExistingFlows(tpAniSirGlobal pMac,
v_U8_t sessionId)
{
tListElem *pEntry= NULL, *pNextEntry = NULL;
sme_QosFlowInfoEntry *flow_info = NULL;
pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_TRUE );
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN,
"%s: %d: Flow List empty, nothing to delete",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
while( pEntry )
{
pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_TRUE );
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
if (flow_info->sessionId == sessionId)
{
if((SME_QOS_REASON_REQ_SUCCESS == flow_info->reason )||
(SME_QOS_REASON_SETUP == flow_info->reason )||
(SME_QOS_REASON_RELEASE == flow_info->reason )||
(SME_QOS_REASON_MODIFY == flow_info->reason ))
{
flow_info->QoSCallback(pMac, flow_info->HDDcontext,
NULL,
SME_QOS_STATUS_RELEASE_QOS_LOST_IND,
flow_info->QosFlowID);
}
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Deleting entry at %pK with flowID %d",
__func__, __LINE__,
flow_info, flow_info->QosFlowID);
//delete the entry from Flow List
csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE );
vos_mem_free(flow_info);
}
pEntry = pNextEntry;
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosBufferCmd() - Utility function to buffer a request (setup/modify/
release) from client while processing another one on the same AC.
\param pcmd - a pointer to the cmd structure to be saved inside the buffered
cmd link list
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosBufferCmd(sme_QosCmdInfo *pcmd, v_BOOL_t insert_head)
{
sme_QosSessionInfo *pSession;
sme_QosCmdInfoEntry * pentry = NULL;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Invoked",
__func__, __LINE__);
pentry = (sme_QosCmdInfoEntry *) vos_mem_malloc(sizeof(sme_QosCmdInfoEntry));
if (!pentry)
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Memory allocation failure",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
// copy the entire CmdInfo
pentry->cmdInfo = *pcmd;
pSession = &sme_QosCb.sessionInfo[pcmd->sessionId];
if(insert_head)
{
csrLLInsertHead(&pSession->bufferedCommandList, &pentry->link, VOS_TRUE);
}
else
{
csrLLInsertTail(&pSession->bufferedCommandList, &pentry->link, VOS_TRUE);
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessBufferedCmd() - Utility function to process a buffered
request (setup/modify/release) initially came from the client.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
static eHalStatus sme_QosProcessBufferedCmd(v_U8_t sessionId)
{
sme_QosSessionInfo *pSession;
sme_QosCmdInfoEntry *pcmd = NULL;
tListElem *pEntry= NULL;
sme_QosStatusType hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
eHalStatus halStatus = eHAL_STATUS_SUCCESS;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Invoked on session %d",
__func__, __LINE__,
sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
if(!csrLLIsListEmpty( &pSession->bufferedCommandList, VOS_FALSE ))
{
pEntry = csrLLRemoveHead( &pSession->bufferedCommandList, VOS_TRUE );
if(!pEntry)
{
//Err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: no more buffered commands on session %d",
__func__, __LINE__,
sessionId);
pSession->readyForPowerSave = VOS_TRUE;
return eHAL_STATUS_FAILURE;
}
pcmd = GET_BASE_ADDR( pEntry, sme_QosCmdInfoEntry, link );
switch(pcmd->cmdInfo.command)
{
case SME_QOS_SETUP_REQ:
hdd_status = sme_QosInternalSetupReq(pcmd->cmdInfo.pMac,
pcmd->cmdInfo.sessionId,
&pcmd->cmdInfo.u.setupCmdInfo.QoSInfo,
pcmd->cmdInfo.u.setupCmdInfo.QoSCallback,
pcmd->cmdInfo.u.setupCmdInfo.HDDcontext,
pcmd->cmdInfo.u.setupCmdInfo.UPType,
pcmd->cmdInfo.u.setupCmdInfo.QosFlowID,
VOS_TRUE,
pcmd->cmdInfo.u.setupCmdInfo.hoRenewal);
if(SME_QOS_STATUS_SETUP_FAILURE_RSP == hdd_status)
{
//Err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: sme_QosInternalSetupReq failed on session %d",
__func__, __LINE__,
sessionId);
halStatus = eHAL_STATUS_FAILURE;
}
break;
case SME_QOS_RELEASE_REQ:
hdd_status = sme_QosInternalReleaseReq(pcmd->cmdInfo.pMac,
pcmd->cmdInfo.sessionId,
pcmd->cmdInfo.u.releaseCmdInfo.QosFlowID,
VOS_TRUE);
if(SME_QOS_STATUS_RELEASE_FAILURE_RSP == hdd_status)
{
//Err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: sme_QosInternalReleaseReq failed on session %d",
__func__, __LINE__,
sessionId);
halStatus = eHAL_STATUS_FAILURE;
}
break;
case SME_QOS_MODIFY_REQ:
hdd_status = sme_QosInternalModifyReq(pcmd->cmdInfo.pMac,
&pcmd->cmdInfo.u.modifyCmdInfo.QoSInfo,
pcmd->cmdInfo.u.modifyCmdInfo.QosFlowID,
VOS_TRUE);
if(SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP == hdd_status)
{
//Err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: sme_QosInternalModifyReq failed on session %d",
__func__, __LINE__,
sessionId);
halStatus = eHAL_STATUS_FAILURE;
}
break;
case SME_QOS_RESEND_REQ:
hdd_status = sme_QosReRequestAddTS(pcmd->cmdInfo.pMac,
pcmd->cmdInfo.sessionId,
&pcmd->cmdInfo.u.resendCmdInfo.QoSInfo,
pcmd->cmdInfo.u.resendCmdInfo.ac,
pcmd->cmdInfo.u.resendCmdInfo.tspecMask);
if(SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP == hdd_status)
{
//Err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: sme_QosReRequestAddTS failed on session %d",
__func__, __LINE__,
sessionId);
halStatus = eHAL_STATUS_FAILURE;
}
break;
default:
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d unknown cmd = %d",
__func__, __LINE__,
sessionId, pcmd->cmdInfo.command);
VOS_ASSERT(0);
break;
}
// buffered command has been processed, reclaim the memory
vos_mem_free(pcmd);
}
else
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: cmd buffer empty",
__func__, __LINE__);
pSession->readyForPowerSave = VOS_TRUE;
}
return halStatus;
}
/*--------------------------------------------------------------------------
\brief sme_QosDeleteBufferedRequests() - Utility function to Delete the buffered
requests in the buffered_cmd_list, if we lost connectivity.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
static eHalStatus sme_QosDeleteBufferedRequests(tpAniSirGlobal pMac,
v_U8_t sessionId)
{
sme_QosSessionInfo *pSession;
sme_QosCmdInfoEntry *pcmd = NULL;
tListElem *pEntry= NULL, *pNextEntry = NULL;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Invoked on session %d",
__func__, __LINE__, sessionId);
pSession = &sme_QosCb.sessionInfo[sessionId];
pEntry = csrLLPeekHead( &pSession->bufferedCommandList, VOS_TRUE );
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_WARN,
"%s: %d: Buffered List empty, nothing to delete on session %d",
__func__, __LINE__,
sessionId);
return eHAL_STATUS_FAILURE;
}
while( pEntry )
{
pNextEntry = csrLLNext( &pSession->bufferedCommandList, pEntry, VOS_TRUE );
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
"%s: %d: deleting entry from buffered List",
__func__, __LINE__);
//delete the entry from Flow List
csrLLRemoveEntry(&pSession->bufferedCommandList, pEntry, VOS_TRUE );
// reclaim the memory
pcmd = GET_BASE_ADDR( pEntry, sme_QosCmdInfoEntry, link );
vos_mem_free(pcmd);
pEntry = pNextEntry;
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosSaveAssocInfo() - Utility function to save the assoc info in the
CB like BSS descriptor of the AP, the profile that HDD sent down with the
connect request, while CSR notifies for assoc/reassoc success.
\param pAssoc_info - pointer to the assoc structure to store the BSS
descriptor of the AP, the profile that HDD sent down with
the connect request
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosSaveAssocInfo(sme_QosSessionInfo *pSession, sme_QosAssocInfo *pAssoc_info)
{
tSirBssDescription *pBssDesc = NULL;
v_U32_t bssLen = 0;
if(NULL == pAssoc_info)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: pAssoc_info is NULL",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
//clean up the assoc info if already set
if(pSession->assocInfo.pBssDesc)
{
vos_mem_free(pSession->assocInfo.pBssDesc);
pSession->assocInfo.pBssDesc = NULL;
}
bssLen = pAssoc_info->pBssDesc->length +
sizeof(pAssoc_info->pBssDesc->length);
//save the bss Descriptor
pBssDesc = (tSirBssDescription *)vos_mem_malloc(bssLen);
if (!pBssDesc)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: couldn't allocate memory for the bss Descriptor",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
vos_mem_copy(pBssDesc, pAssoc_info->pBssDesc, bssLen);
pSession->assocInfo.pBssDesc = pBssDesc;
//save the apsd info from assoc
if(pAssoc_info->pProfile)
{
pSession->apsdMask |= pAssoc_info->pProfile->uapsd_mask;
}
// [TODO] Do we need to update the global APSD bitmap?
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosSetupFnp() - Utility function (pointer) to notify other entries
in FLOW list on the same AC that qos params got modified
\param pMac - Pointer to the global MAC parameter structure.
\param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure)
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosSetupFnp(tpAniSirGlobal pMac, tListElem *pEntry)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosFlowInfoEntry *flow_info = NULL;
sme_QosStatusType hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND;
sme_QosEdcaAcType ac;
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Entry is NULL",
__func__, __LINE__);
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
ac = flow_info->ac_type;
pSession = &sme_QosCb.sessionInfo[flow_info->sessionId];
pACInfo = &pSession->ac_info[ac];
if(SME_QOS_REASON_REQ_SUCCESS == flow_info->reason)
{
//notify HDD, only the other Flows running on the AC
flow_info->QoSCallback(pMac, flow_info->HDDcontext,
&pACInfo->curr_QoSInfo[flow_info->tspec_mask - 1],
hdd_status,
flow_info->QosFlowID);
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Entry with flowID = %d getting notified",
__func__, __LINE__,
flow_info->QosFlowID);
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosModificationNotifyFnp() - Utility function (pointer) to notify
other entries in FLOW list on the same AC that qos params got modified
\param pMac - Pointer to the global MAC parameter structure.
\param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure)
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosModificationNotifyFnp(tpAniSirGlobal pMac, tListElem *pEntry)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosFlowInfoEntry *flow_info = NULL;
sme_QosStatusType hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND;
sme_QosEdcaAcType ac;
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Entry is NULL",
__func__, __LINE__);
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
ac = flow_info->ac_type;
pSession = &sme_QosCb.sessionInfo[flow_info->sessionId];
pACInfo = &pSession->ac_info[ac];
if(SME_QOS_REASON_REQ_SUCCESS == flow_info->reason)
{
//notify HDD, only the other Flows running on the AC
flow_info->QoSCallback(pMac, flow_info->HDDcontext,
&pACInfo->curr_QoSInfo[flow_info->tspec_mask - 1],
hdd_status,
flow_info->QosFlowID);
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Entry with flowID = %d getting notified",
__func__, __LINE__,
flow_info->QosFlowID);
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosModifyFnp() - Utility function (pointer) to delete the original
entry in FLOW list & add the modified one
\param pMac - Pointer to the global MAC parameter structure.
\param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure)
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosModifyFnp(tpAniSirGlobal pMac, tListElem *pEntry)
{
sme_QosFlowInfoEntry *flow_info = NULL;
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Entry is NULL",
__func__, __LINE__);
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
switch(flow_info->reason)
{
case SME_QOS_REASON_MODIFY_PENDING:
//set the proper reason code for the new (with modified params) entry
flow_info->reason = SME_QOS_REASON_REQ_SUCCESS;
break;
case SME_QOS_REASON_MODIFY:
//delete the original entry from Flow List
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Deleting original entry at %pK with flowID %d",
__func__, __LINE__,
flow_info, flow_info->QosFlowID);
csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE );
// reclaim the memory
vos_mem_free(flow_info);
break;
default:
break;
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosDelTsIndFnp() - Utility function (pointer) to find all Flows on
the particular AC & delete them, also send HDD indication through the callback
it registered per request
\param pMac - Pointer to the global MAC parameter structure.
\param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure)
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosDelTsIndFnp(tpAniSirGlobal pMac, tListElem *pEntry)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosFlowInfoEntry *flow_info = NULL;
sme_QosEdcaAcType ac;
eHalStatus lock_status = eHAL_STATUS_FAILURE;
sme_QosStatusType status;
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Entry is NULL",
__func__, __LINE__);
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
//delete the entry from Flow List
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
ac = flow_info->ac_type;
pSession = &sme_QosCb.sessionInfo[flow_info->sessionId];
pACInfo = &pSession->ac_info[ac];
pACInfo->relTrig = SME_QOS_RELEASE_BY_AP;
lock_status = sme_AcquireGlobalLock( &pMac->sme );
if ( !HAL_STATUS_SUCCESS( lock_status ) )
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Unable to obtain lock",
__func__, __LINE__);
return SME_QOS_STATUS_RELEASE_FAILURE_RSP;
}
//Call the internal function for QoS release, adding a layer of abstraction
status = sme_QosInternalReleaseReq(pMac, flow_info->sessionId,
flow_info->QosFlowID, VOS_FALSE);
sme_ReleaseGlobalLock( &pMac->sme );
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: QoS Release return status on Flow %d is %d",
__func__, __LINE__,
flow_info->QosFlowID, status);
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosReassocSuccessEvFnp() - Utility function (pointer) to notify HDD
the success for the requested flow & notify all the other flows running on the
same AC that QoS params got modified
\param pMac - Pointer to the global MAC parameter structure.
\param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure)
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosReassocSuccessEvFnp(tpAniSirGlobal pMac, tListElem *pEntry)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosFlowInfoEntry *flow_info = NULL;
v_BOOL_t delete_entry = VOS_FALSE;
sme_QosStatusType hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
sme_QosEdcaAcType ac;
eHalStatus pmc_status = eHAL_STATUS_FAILURE;
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Entry is NULL",
__func__, __LINE__);
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
flow_info = GET_BASE_ADDR(pEntry, sme_QosFlowInfoEntry, link);
ac = flow_info->ac_type;
pSession = &sme_QosCb.sessionInfo[flow_info->sessionId];
pACInfo = &pSession->ac_info[ac];
switch(flow_info->reason)
{
case SME_QOS_REASON_SETUP:
hdd_status = SME_QOS_STATUS_SETUP_SUCCESS_IND;
delete_entry = VOS_FALSE;
flow_info->reason = SME_QOS_REASON_REQ_SUCCESS;
//check for the case where we had to do reassoc to reset the apsd bit
//for the ac - release or modify scenario
if(pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0].ts_info.psb)
{
// notify PMC as App is looking for APSD. If we already requested
// then we don't need to do anything.
if(!pSession->uapsdAlreadyRequested)
{
// this is the first flow to detect we need PMC in UAPSD mode
if(!pMac->psOffloadEnabled)
{
pmc_status = pmcStartUapsd(pMac,
sme_QosPmcStartUapsdCallback,
pSession);
}
else
{
pmc_status = pmcOffloadStartUapsd(pMac,
flow_info->sessionId,
sme_QosPmcOffloadStartUapsdCallback,
pSession);
}
// if PMC doesn't return success right away means it is yet to put
// the module in BMPS state & later to UAPSD state
if(eHAL_STATUS_FAILURE == pmc_status)
{
hdd_status = SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_SET_FAILED;
//we need to always notify this case
flow_info->hoRenewal = VOS_FALSE;
}
else if(eHAL_STATUS_PMC_PENDING == pmc_status)
{
// let other flows know PMC has been notified
pSession->uapsdAlreadyRequested = VOS_TRUE;
}
// for any other pmc status we declare success
}
}
break;
case SME_QOS_REASON_RELEASE:
pACInfo->num_flows[SME_QOS_TSPEC_INDEX_0]--;
// fall through
case SME_QOS_REASON_MODIFY:
delete_entry = VOS_TRUE;
break;
case SME_QOS_REASON_MODIFY_PENDING:
hdd_status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND;
delete_entry = VOS_FALSE;
flow_info->reason = SME_QOS_REASON_REQ_SUCCESS;
if(pACInfo->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0].ts_info.psb)
{
if(!pSession->uapsdAlreadyRequested)
{
if(!pMac->psOffloadEnabled)
{
// this is the first flow to detect we need PMC in UAPSD mode
pmc_status = pmcStartUapsd(pMac,
sme_QosPmcStartUapsdCallback,
pSession);
}
else
{
// this is the first flow to detect we need PMC in UAPSD mode
pmc_status = pmcOffloadStartUapsd(pMac,
flow_info->sessionId,
sme_QosPmcOffloadStartUapsdCallback,
pSession);
}
// if PMC doesn't return success right away means it is yet to put
// the module in BMPS state & later to UAPSD state
if(eHAL_STATUS_FAILURE == pmc_status)
{
hdd_status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_SET_FAILED;
// we need to always notify this case
flow_info->hoRenewal = VOS_FALSE;
}
else if(eHAL_STATUS_PMC_PENDING == pmc_status)
{
pSession->uapsdAlreadyRequested = VOS_TRUE;
}
// for any other pmc status we declare success
}
}
break;
case SME_QOS_REASON_REQ_SUCCESS:
hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND;
// fall through
default:
delete_entry = VOS_FALSE;
break;
}
if(!delete_entry)
{
if(!flow_info->hoRenewal)
{
flow_info->QoSCallback(pMac, flow_info->HDDcontext,
&pACInfo->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0],
hdd_status,
flow_info->QosFlowID);
}
else
{
flow_info->hoRenewal = VOS_FALSE;
}
}
else
{
//delete the entry from Flow List
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Deleting entry at %pK with flowID %d",
__func__, __LINE__,
flow_info, flow_info->QosFlowID);
csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE );
// reclaim the memory
vos_mem_free(flow_info);
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosAddTsFailureFnp() - Utility function (pointer),
if the Addts request was for for an flow setup request, delete the entry from
Flow list & notify HDD
if the Addts request was for downgrading of QoS params because of an flow
release requested on the AC, delete the entry from Flow list & notify HDD
if the Addts request was for change of QoS params because of an flow
modification requested on the AC, delete the new entry from Flow list & notify
HDD
\param pMac - Pointer to the global MAC parameter structure.
\param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure)
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosAddTsFailureFnp(tpAniSirGlobal pMac, tListElem *pEntry)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosFlowInfoEntry *flow_info = NULL;
v_BOOL_t inform_hdd = VOS_FALSE;
sme_QosStatusType hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
sme_QosEdcaAcType ac;
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Entry is NULL",
__func__, __LINE__);
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
ac = flow_info->ac_type;
pSession = &sme_QosCb.sessionInfo[flow_info->sessionId];
pACInfo = &pSession->ac_info[ac];
switch(flow_info->reason)
{
case SME_QOS_REASON_SETUP:
hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
pACInfo->num_flows[pACInfo->tspec_pending - 1]--;
inform_hdd = VOS_TRUE;
break;
case SME_QOS_REASON_RELEASE:
hdd_status = SME_QOS_STATUS_RELEASE_FAILURE_RSP;
pACInfo->num_flows[pACInfo->tspec_pending - 1]--;
inform_hdd = VOS_TRUE;
break;
case SME_QOS_REASON_MODIFY_PENDING:
hdd_status = SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP;
inform_hdd = VOS_TRUE;
break;
case SME_QOS_REASON_MODIFY:
flow_info->reason = SME_QOS_REASON_REQ_SUCCESS;
case SME_QOS_REASON_REQ_SUCCESS:
default:
inform_hdd = VOS_FALSE;
break;
}
if(inform_hdd)
{
//notify HDD, only the requested Flow, other Flows running on the AC stay
// intact
if(!flow_info->hoRenewal)
{
flow_info->QoSCallback(pMac, flow_info->HDDcontext,
&pACInfo->curr_QoSInfo[pACInfo->tspec_pending - 1],
hdd_status,
flow_info->QosFlowID);
}
else
{
flow_info->QoSCallback(pMac, flow_info->HDDcontext,
&pACInfo->curr_QoSInfo[pACInfo->tspec_pending - 1],
SME_QOS_STATUS_RELEASE_QOS_LOST_IND,
flow_info->QosFlowID);
}
//delete the entry from Flow List
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Deleting entry at %pK with flowID %d",
__func__, __LINE__,
flow_info, flow_info->QosFlowID);
csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE );
// reclaim the memory
vos_mem_free(flow_info);
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosAddTsSuccessFnp() - Utility function (pointer),
if the Addts request was for for an flow setup request, notify HDD for success
for the flow & notify all the other flows running on the same AC that QoS
params got modified
if the Addts request was for downgrading of QoS params because of an flow
release requested on the AC, delete the entry from Flow list & notify HDD
if the Addts request was for change of QoS params because of an flow
modification requested on the AC, delete the old entry from Flow list & notify
HDD for success for the flow & notify all the other flows running on the same
AC that QoS params got modified
\param pMac - Pointer to the global MAC parameter structure.
\param pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure)
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosAddTsSuccessFnp(tpAniSirGlobal pMac, tListElem *pEntry)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosFlowInfoEntry *flow_info = NULL;
v_BOOL_t inform_hdd = VOS_FALSE;
v_BOOL_t delete_entry = VOS_FALSE;
sme_QosStatusType hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
sme_QosEdcaAcType ac;
eHalStatus pmc_status = eHAL_STATUS_FAILURE;
tCsrRoamModifyProfileFields modifyProfileFields;
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Entry is NULL",
__func__, __LINE__);
VOS_ASSERT(0);
return eHAL_STATUS_FAILURE;
}
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
ac = flow_info->ac_type;
pSession = &sme_QosCb.sessionInfo[flow_info->sessionId];
pACInfo = &pSession->ac_info[ac];
if(flow_info->tspec_mask != pACInfo->tspec_pending)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: No need to notify the HDD, the ADDTS "
"success is not for index = %d of the AC = %d",
__func__, __LINE__,
flow_info->tspec_mask, ac);
return eHAL_STATUS_SUCCESS;
}
switch(flow_info->reason)
{
case SME_QOS_REASON_SETUP:
hdd_status = SME_QOS_STATUS_SETUP_SUCCESS_IND;
flow_info->reason = SME_QOS_REASON_REQ_SUCCESS;
delete_entry = VOS_FALSE;
inform_hdd = VOS_TRUE;
// check if App is looking for APSD
if(pACInfo->requested_QoSInfo[pACInfo->tspec_pending - 1].ts_info.psb)
{
// notify PMC as App is looking for APSD. If we already requested
// then we don't need to do anything
if(!pSession->uapsdAlreadyRequested)
{
if(!pMac->psOffloadEnabled)
{
// this is the first flow to detect we need PMC in UAPSD mode
pmc_status = pmcStartUapsd(pMac,
sme_QosPmcStartUapsdCallback,
pSession);
}
else
{
// this is the first flow to detect we need PMC in UAPSD mode
pmc_status = pmcOffloadStartUapsd(pMac,
flow_info->sessionId,
sme_QosPmcOffloadStartUapsdCallback,
pSession);
}
// if PMC doesn't return success right away means it is yet to put
// the module in BMPS state & later to UAPSD state
if(eHAL_STATUS_FAILURE == pmc_status)
{
hdd_status = SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_SET_FAILED;
// we need to always notify this case
flow_info->hoRenewal = VOS_FALSE;
}
else if(eHAL_STATUS_PMC_PENDING == pmc_status)
{
// let other flows know PMC has been notified
pSession->uapsdAlreadyRequested = VOS_TRUE;
}
// for any other pmc status we declare success
}
}
break;
case SME_QOS_REASON_RELEASE:
pACInfo->num_flows[pACInfo->tspec_pending - 1]--;
hdd_status = SME_QOS_STATUS_RELEASE_SUCCESS_RSP;
inform_hdd = VOS_TRUE;
delete_entry = VOS_TRUE;
break;
case SME_QOS_REASON_MODIFY:
delete_entry = VOS_TRUE;
inform_hdd = VOS_FALSE;
break;
case SME_QOS_REASON_MODIFY_PENDING:
hdd_status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND;
delete_entry = VOS_FALSE;
flow_info->reason = SME_QOS_REASON_REQ_SUCCESS;
inform_hdd = VOS_TRUE;
//notify PMC if App is looking for APSD
if(pACInfo->requested_QoSInfo[pACInfo->tspec_pending - 1].ts_info.psb)
{
// notify PMC as App is looking for APSD. If we already requested
// then we don't need to do anything.
if(!pSession->uapsdAlreadyRequested)
{
if(!pMac->psOffloadEnabled)
{
// this is the first flow to detect we need PMC in UAPSD mode
pmc_status = pmcStartUapsd(pMac,
sme_QosPmcStartUapsdCallback,
pSession);
}
else
{
// this is the first flow to detect we need PMC in UAPSD mode
pmc_status = pmcOffloadStartUapsd(pMac,
flow_info->sessionId,
sme_QosPmcOffloadStartUapsdCallback,
pSession);
}
// if PMC doesn't return success right away means it is yet to put
// the module in BMPS state & later to UAPSD state
if(eHAL_STATUS_FAILURE == pmc_status)
{
hdd_status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND_APSD_SET_FAILED;
// we need to always notify this case
flow_info->hoRenewal = VOS_FALSE;
}
else if(eHAL_STATUS_PMC_PENDING == pmc_status)
{
// let other flows know PMC has been notified
pSession->uapsdAlreadyRequested = VOS_TRUE;
}
// for any other pmc status we declare success
}
}
else
{
if((pACInfo->num_flows[flow_info->tspec_mask - 1] == 1) &&
(SME_QOS_TSPEC_MASK_BIT_1_2_SET != pACInfo->tspec_mask_status))
{
// this is the only TSPEC active on this AC
// so indicate that we no longer require APSD
pSession->apsdMask &= ~(1 << (SME_QOS_EDCA_AC_VO - ac));
//Also update modifyProfileFields.uapsd_mask in CSR for consistency
csrGetModifyProfileFields(pMac, flow_info->sessionId, &modifyProfileFields);
modifyProfileFields.uapsd_mask = pSession->apsdMask;
csrSetModifyProfileFields(pMac, flow_info->sessionId, &modifyProfileFields);
if(!pSession->apsdMask)
{
if(!pMac->psOffloadEnabled)
{
// this session no longer needs UAPSD
// do any sessions still require UAPSD?
if (!sme_QosIsUapsdActive())
{
// No sessions require UAPSD so turn it off
// (really don't care when PMC stops it)
(void)pmcStopUapsd(pMac);
}
}
else
{
(void)pmcOffloadStopUapsd(pMac, flow_info->sessionId);
}
}
}
}
break;
case SME_QOS_REASON_REQ_SUCCESS:
hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND;
inform_hdd = VOS_TRUE;
default:
delete_entry = VOS_FALSE;
break;
}
if(inform_hdd)
{
if(!flow_info->hoRenewal)
{
flow_info->QoSCallback(pMac, flow_info->HDDcontext,
&pACInfo->curr_QoSInfo[pACInfo->tspec_pending - 1],
hdd_status,
flow_info->QosFlowID);
}
else
{
/*
* For downgrading purpose, Hdd set WmmTspecValid to false during
* roaming. Need to set that flag. Call the hdd callback in successful
* case.
*/
if ((hdd_status == SME_QOS_STATUS_SETUP_SUCCESS_IND)
#if defined (WLAN_FEATURE_VOWIFI_11R)
&&
(!csrRoamIs11rAssoc(pMac, flow_info->sessionId))
#endif
#if defined(FEATURE_WLAN_ESE)
&&
(!csrRoamIsESEAssoc(pMac, flow_info->sessionId))
#endif
)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s calling hdd_wmm_smecallback during roaming for ac = %d", __func__, ac);
flow_info->QoSCallback(pMac, flow_info->HDDcontext,
&pACInfo->curr_QoSInfo[pACInfo->tspec_pending - 1],
hdd_status,
flow_info->QosFlowID
);
}
flow_info->hoRenewal = VOS_FALSE;
}
}
if(delete_entry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Deleting entry at %pK with flowID %d",
__func__, __LINE__,
flow_info, flow_info->QosFlowID);
//delete the entry from Flow List
csrLLRemoveEntry(&sme_QosCb.flow_list, pEntry, VOS_TRUE );
// reclaim the memory
vos_mem_free(flow_info);
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosIsRspPending() - Utility function to check if we are waiting
for an AddTS or reassoc response on some AC other than the given AC
\param sessionId - Session we are interested in
\param ac - Enumeration of the various EDCA Access Categories.
\return boolean
TRUE - Response is pending on an AC
\sa
--------------------------------------------------------------------------*/
static v_BOOL_t sme_QosIsRspPending(v_U8_t sessionId, sme_QosEdcaAcType ac)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosEdcaAcType acIndex;
v_BOOL_t status = VOS_FALSE;
pSession = &sme_QosCb.sessionInfo[sessionId];
for(acIndex = SME_QOS_EDCA_AC_BE; acIndex < SME_QOS_EDCA_AC_MAX; acIndex++)
{
if(acIndex == ac)
{
continue;
}
pACInfo = &pSession->ac_info[acIndex];
if((pACInfo->tspec_pending) || (pACInfo->reassoc_pending))
{
status = VOS_TRUE;
break;
}
}
return status;
}
/*--------------------------------------------------------------------------
\brief sme_QosUpdateHandOff() - Function which can be called to update
Hand-off state of SME QoS Session
\param sessionId - session id
\param updateHandOff - value True/False to update the handoff flag
\sa
-------------------------------------------------------------------------*/
void sme_QosUpdateHandOff(v_U8_t sessionId,
v_BOOL_t updateHandOff)
{
sme_QosSessionInfo *pSession;
pSession = &sme_QosCb.sessionInfo[sessionId];
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_MED,
"%s: %d: handoffRequested %d updateHandOff %d",
__func__, __LINE__,pSession->handoffRequested,
updateHandOff);
pSession->handoffRequested = updateHandOff;
}
/*--------------------------------------------------------------------------
\brief sme_QosIsUapsdActive() - Function which can be called to determine
if any sessions require PMC to be in U-APSD mode.
\return boolean
Returns true if at least one session required PMC to be in U-APSD mode
Returns false if no sessions require PMC to be in U-APSD mode
\sa
--------------------------------------------------------------------------*/
static v_BOOL_t sme_QosIsUapsdActive(void)
{
sme_QosSessionInfo *pSession;
v_U8_t sessionId;
for (sessionId = 0; sessionId < CSR_ROAM_SESSION_MAX; ++sessionId)
{
pSession = &sme_QosCb.sessionInfo[sessionId];
if ((pSession->sessionActive) && (pSession->apsdMask))
{
return VOS_TRUE;
}
}
// no active sessions have U-APSD active
return VOS_FALSE;
}
/*--------------------------------------------------------------------------
\brief sme_QosPmcFullPowerCallback() - Callback function registered with PMC
to notify SME-QoS when it puts the chip into full power
\param callbackContext - The context passed to PMC during pmcRequestFullPower
call.
\param status - eHalStatus returned by PMC.
\return None
\sa
--------------------------------------------------------------------------*/
void sme_QosPmcFullPowerCallback(void *callbackContext, eHalStatus status)
{
sme_QosSessionInfo *pSession = callbackContext;
if(HAL_STATUS_SUCCESS(status))
{
(void)sme_QosProcessBufferedCmd(pSession->sessionId);
}
else
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: PMC failed to put the chip in Full power",
__func__, __LINE__);
VOS_ASSERT(0);
}
}
void sme_QosPmcOffloadFullPowerCallback(void *callbackContext, tANI_U32 sessionId,
eHalStatus status)
{
sme_QosSessionInfo *pSession = callbackContext;
if(HAL_STATUS_SUCCESS(status))
{
(void)sme_QosProcessBufferedCmd(pSession->sessionId);
}
else
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: PMC failed to put the chip in Full power",
__func__, __LINE__);
VOS_ASSERT(0);
}
}
/*--------------------------------------------------------------------------
\brief sme_QosPmcStartUAPSDCallback() - Callback function registered with PMC
to notify SME-QoS when it puts the chip into UAPSD mode
\param callbackContext - The context passed to PMC during pmcStartUapsd call.
\param status - eHalStatus returned by PMC.
\return None
\sa
--------------------------------------------------------------------------*/
void sme_QosPmcStartUapsdCallback(void *callbackContext, eHalStatus status)
{
sme_QosSessionInfo *pSession = callbackContext;
// NOTE WELL
//
// In the original QoS design the TL module was responsible for
// the generation of trigger frames. When that design was in
// use, we had to queue up any flows which were waiting for PMC
// since we didn't want to notify HDD until PMC had changed to
// UAPSD state. Otherwise HDD would provide TL with the trigger
// frame parameters, and TL would start trigger frame generation
// before PMC was ready. The flows were queued in various places
// throughout this module, and they were dequeued here following
// a successful transition to the UAPSD state by PMC.
//
// In the current QoS design the Firmware is responsible for the
// generation of trigger frames, but the parameters are still
// provided by TL via HDD. The Firmware will be notified of the
// change to UAPSD state directly by PMC, at which time it will be
// responsible for the generation of trigger frames. Therefore
// where we used to queue up flows waiting for PMC to transition
// to the UAPSD state, we now always transition directly to the
// "success" state so that HDD will immediately provide the trigger
// frame parameters to TL, who will in turn plumb them down to the
// Firmware. That way the Firmware will have the trigger frame
// parameters when it needs them
// just note that there is no longer an outstanding request
pSession->uapsdAlreadyRequested = VOS_FALSE;
}
void sme_QosPmcOffloadStartUapsdCallback(void *callbackContext,
tANI_U32 sessionId, eHalStatus status)
{
sme_QosSessionInfo *pSession = callbackContext;
pSession->uapsdAlreadyRequested = VOS_FALSE;
}
/*--------------------------------------------------------------------------
\brief sme_QosPmcCheckRoutine() - Function registered with PMC to check with
SME-QoS whenever the device is about to enter one of the power
save modes. PMC runs a poll with all the registered modules if device can
enter power save mode or remain in full power
\param callbackContext - The context passed to PMC during registration through
pmcRegisterPowerSaveCheck.
\return boolean
SME-QOS returns PMC true or false respectively if it wants to vote for
entering power save or not
\sa
--------------------------------------------------------------------------*/
v_BOOL_t sme_QosPmcCheckRoutine(void *callbackContext)
{
sme_QosSessionInfo *pSession;
v_U8_t sessionId;
for (sessionId = 0; sessionId < CSR_ROAM_SESSION_MAX; ++sessionId)
{
pSession = &sme_QosCb.sessionInfo[sessionId];
if ((pSession->sessionActive) &&
(!pSession->readyForPowerSave))
{
return VOS_FALSE;
}
}
/* All active sessions have voted for power save */
return VOS_TRUE;
}
v_BOOL_t sme_QosPmcOffloadCheckRoutine(void *callbackContext, tANI_U32 sessionId)
{
sme_QosSessionInfo *pSession = &sme_QosCb.sessionInfo[sessionId];
if ((pSession->sessionActive) &&
(!pSession->readyForPowerSave))
{
return VOS_FALSE;
}
return VOS_TRUE;
}
/*--------------------------------------------------------------------------
\brief sme_QosPmcDeviceStateUpdateInd() - Callback function registered with
PMC to notify SME-QoS when it changes the power state
\param callbackContext - The context passed to PMC during registration
through pmcRegisterDeviceStateUpdateInd.
\param pmcState - Current power state that PMC moved into.
\return None
\sa
--------------------------------------------------------------------------*/
void sme_QosPmcDeviceStateUpdateInd(void *callbackContext, tPmcState pmcState)
{
eHalStatus status = eHAL_STATUS_FAILURE;
tpAniSirGlobal pMac = PMAC_STRUCT( callbackContext );
//check all the entries in Flow list for non-zero service interval, which will
//tell us if we need to notify HDD when PMC is out of UAPSD mode or going
// back to UAPSD mode
switch(pmcState)
{
case FULL_POWER:
status = sme_QosProcessOutOfUapsdMode(pMac);
break;
case UAPSD:
status = sme_QosProcessIntoUapsdMode(pMac);
break;
default:
status = eHAL_STATUS_SUCCESS;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
"%s: %d: nothing to process in PMC state %s (%d)",
__func__, __LINE__,
sme_PmcStatetoString(pmcState), pmcState);
}
if(!HAL_STATUS_SUCCESS(status))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: ignoring Device(PMC) state change to %s (%d)",
__func__, __LINE__,
sme_PmcStatetoString(pmcState), pmcState);
}
}
void sme_OffloadQosPmcDeviceStateUpdateInd(void *callbackContext,
tANI_U32 sessionId, tPmcState pmcState)
{
eHalStatus status = eHAL_STATUS_FAILURE;
tpAniSirGlobal pMac = PMAC_STRUCT( callbackContext );
/*
* check all the entries in Flow list for non-zero service interval,
* which will tell us if we need to notify HDD when
* PMC is out of UAPSD mode or going
* back to UAPSD mode
*/
switch(pmcState)
{
case FULL_POWER:
status = sme_OffloadQosProcessOutOfUapsdMode(pMac, sessionId);
break;
case UAPSD:
status = sme_OffloadQosProcessIntoUapsdMode(pMac, sessionId);
break;
default:
status = eHAL_STATUS_SUCCESS;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: nothing to process in PMC state %d",
__func__, __LINE__,
pmcState);
}
if(!HAL_STATUS_SUCCESS(status))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: ignoring Device(PMC) state change to %d",
__func__, __LINE__,
pmcState);
}
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessOutOfUapsdMode() - Function to notify HDD when PMC
notifies SME-QoS that it moved out of UAPSD mode to FULL power
\param pMac - Pointer to the global MAC parameter structure.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessOutOfUapsdMode(tpAniSirGlobal pMac)
{
sme_QosSessionInfo *pSession;
tListElem *pEntry= NULL, *pNextEntry = NULL;
sme_QosFlowInfoEntry *flow_info = NULL;
pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE );
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Flow List empty, can't search",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
while( pEntry )
{
pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE );
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
pSession = &sme_QosCb.sessionInfo[flow_info->sessionId];
//only notify the flows which already successfully setup UAPSD
if((flow_info->QoSInfo.max_service_interval ||
flow_info->QoSInfo.min_service_interval) &&
(SME_QOS_REASON_REQ_SUCCESS == flow_info->reason))
{
flow_info->QoSCallback(pMac, flow_info->HDDcontext,
&pSession->ac_info[flow_info->ac_type].curr_QoSInfo[flow_info->tspec_mask - 1],
SME_QOS_STATUS_OUT_OF_APSD_POWER_MODE_IND,
flow_info->QosFlowID);
}
pEntry = pNextEntry;
}
return eHAL_STATUS_SUCCESS;
}
eHalStatus sme_OffloadQosProcessOutOfUapsdMode(tpAniSirGlobal pMac,
tANI_U32 sessionId)
{
sme_QosSessionInfo *pSession;
tListElem *pEntry= NULL, *pNextEntry = NULL;
sme_QosFlowInfoEntry *flow_info = NULL;
pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE );
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Flow List empty, can't search",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
while(pEntry)
{
pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE );
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
pSession = &sme_QosCb.sessionInfo[flow_info->sessionId];
/* only notify the flows which already successfully setup UAPSD */
if((sessionId == flow_info->sessionId) &&
(flow_info->QoSInfo.max_service_interval ||
flow_info->QoSInfo.min_service_interval) &&
(SME_QOS_REASON_REQ_SUCCESS == flow_info->reason))
{
flow_info->QoSCallback(pMac, flow_info->HDDcontext,
&pSession->ac_info[flow_info->ac_type].
curr_QoSInfo[flow_info->tspec_mask - 1],
SME_QOS_STATUS_OUT_OF_APSD_POWER_MODE_IND,
flow_info->QosFlowID);
}
pEntry = pNextEntry;
}
return eHAL_STATUS_SUCCESS;
}
/*--------------------------------------------------------------------------
\brief sme_QosProcessIntoUapsdMode() - Function to notify HDD when PMC
notifies SME-QoS that it is moving into UAPSD mode
\param pMac - Pointer to the global MAC parameter structure.
\return eHalStatus
\sa
--------------------------------------------------------------------------*/
eHalStatus sme_QosProcessIntoUapsdMode(tpAniSirGlobal pMac)
{
sme_QosSessionInfo *pSession;
tListElem *pEntry= NULL, *pNextEntry = NULL;
sme_QosFlowInfoEntry *flow_info = NULL;
pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE );
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Flow List empty, can't search",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
while( pEntry )
{
pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE );
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
pSession = &sme_QosCb.sessionInfo[flow_info->sessionId];
//only notify the flows which already successfully setup UAPSD
if( (flow_info->QoSInfo.ts_info.psb) &&
(SME_QOS_REASON_REQ_SUCCESS == flow_info->reason) )
{
flow_info->QoSCallback(pMac, flow_info->HDDcontext,
&pSession->ac_info[flow_info->ac_type].curr_QoSInfo[flow_info->tspec_mask - 1],
SME_QOS_STATUS_INTO_APSD_POWER_MODE_IND,
flow_info->QosFlowID);
}
pEntry = pNextEntry;
}
return eHAL_STATUS_SUCCESS;
}
eHalStatus sme_OffloadQosProcessIntoUapsdMode(tpAniSirGlobal pMac, tANI_U32 sessionId)
{
sme_QosSessionInfo *pSession;
tListElem *pEntry= NULL, *pNextEntry = NULL;
sme_QosFlowInfoEntry *flow_info = NULL;
pEntry = csrLLPeekHead( &sme_QosCb.flow_list, VOS_FALSE );
if(!pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Flow List empty, can't search",
__func__, __LINE__);
return eHAL_STATUS_FAILURE;
}
while(pEntry)
{
pNextEntry = csrLLNext( &sme_QosCb.flow_list, pEntry, VOS_FALSE );
flow_info = GET_BASE_ADDR( pEntry, sme_QosFlowInfoEntry, link );
pSession = &sme_QosCb.sessionInfo[flow_info->sessionId];
/* only notify the flows which already successfully setup UAPSD */
if((sessionId == flow_info->sessionId) &&
(flow_info->QoSInfo.max_service_interval ||
flow_info->QoSInfo.min_service_interval) &&
(SME_QOS_REASON_REQ_SUCCESS == flow_info->reason))
{
flow_info->QoSCallback(pMac, flow_info->HDDcontext,
&pSession->ac_info[flow_info->ac_type].
curr_QoSInfo[flow_info->tspec_mask - 1],
SME_QOS_STATUS_INTO_APSD_POWER_MODE_IND,
flow_info->QosFlowID);
}
pEntry = pNextEntry;
}
return eHAL_STATUS_SUCCESS;
}
void sme_QosCleanupCtrlBlkForHandoff(tpAniSirGlobal pMac, v_U8_t sessionId)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosEdcaAcType ac;
pSession = &sme_QosCb.sessionInfo[sessionId];
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
vos_mem_zero(pACInfo->curr_QoSInfo,
sizeof(sme_QosWmmTspecInfo) * SME_QOS_TSPEC_INDEX_MAX);
vos_mem_zero(pACInfo->requested_QoSInfo,
sizeof(sme_QosWmmTspecInfo) * SME_QOS_TSPEC_INDEX_MAX);
pACInfo->num_flows[0] = 0;
pACInfo->num_flows[1] = 0;
pACInfo->reassoc_pending = VOS_FALSE;
pACInfo->tspec_mask_status = 0;
pACInfo->tspec_pending = VOS_FALSE;
pACInfo->hoRenewal = VOS_FALSE;
pACInfo->prev_state = SME_QOS_LINK_UP;
}
}
/*--------------------------------------------------------------------------
\brief sme_QosIsTSInfoAckPolicyValid() - The SME QoS API exposed to HDD to
check if TS info ack policy field can be set to "HT-immediate block
acknowledgment"
\param pMac - The handle returned by macOpen.
\param pQoSInfo - Pointer to sme_QosWmmTspecInfo which contains the WMM TSPEC
related info, provided by HDD
\param sessionId - sessionId returned by sme_OpenSession.
\return VOS_TRUE - Current Association is HT association and so TS info ack policy
can be set to "HT-immediate block acknowledgment"
\sa
--------------------------------------------------------------------------*/
v_BOOL_t sme_QosIsTSInfoAckPolicyValid(tpAniSirGlobal pMac,
sme_QosWmmTspecInfo * pQoSInfo,
v_U8_t sessionId)
{
tDot11fBeaconIEs *pIes = NULL;
sme_QosSessionInfo *pSession;
eHalStatus hstatus;
if( !CSR_IS_SESSION_VALID( pMac, sessionId ) )
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Session Id %d is invalid",
__func__, __LINE__,
sessionId);
return VOS_FALSE;
}
pSession = &sme_QosCb.sessionInfo[sessionId];
if( !pSession->sessionActive )
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Session %d is inactive",
__func__, __LINE__,
sessionId);
return VOS_FALSE;
}
if(!pSession->assocInfo.pBssDesc)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Session %d has an Invalid BSS Descriptor",
__func__, __LINE__,
sessionId);
return VOS_FALSE;
}
hstatus = csrGetParsedBssDescriptionIEs(pMac,
pSession->assocInfo.pBssDesc,
&pIes);
if(!HAL_STATUS_SUCCESS(hstatus))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d unable to parse BSS IEs",
__func__, __LINE__,
sessionId);
return VOS_FALSE;
}
/* success means pIes was allocated */
if(!pIes->HTCaps.present &&
pQoSInfo->ts_info.ack_policy == SME_QOS_WMM_TS_ACK_POLICY_HT_IMMEDIATE_BLOCK_ACK)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d HT Caps aren't present but application set ack policy to HT ",
__func__, __LINE__,
sessionId);
vos_mem_free(pIes);
return VOS_FALSE;
}
vos_mem_free(pIes);
return VOS_TRUE;
}
v_BOOL_t sme_QosValidateRequestedParams(tpAniSirGlobal pMac,
sme_QosWmmTspecInfo * pQoSInfo,
v_U8_t sessionId)
{
v_BOOL_t rc = VOS_FALSE;
do
{
if(SME_QOS_WMM_TS_DIR_RESV == pQoSInfo->ts_info.direction) break;
if(!sme_QosIsTSInfoAckPolicyValid(pMac, pQoSInfo, sessionId)) break;
rc = VOS_TRUE;
}while(0);
return rc;
}
static eHalStatus qosIssueCommand( tpAniSirGlobal pMac, v_U8_t sessionId,
eSmeCommandType cmdType, sme_QosWmmTspecInfo * pQoSInfo,
sme_QosEdcaAcType ac, v_U8_t tspec_mask )
{
eHalStatus status = eHAL_STATUS_RESOURCES;
tSmeCmd *pCommand = NULL;
do
{
pCommand = smeGetCommandBuffer( pMac );
if ( !pCommand )
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: fail to get command buffer for command %d",
__func__, __LINE__, cmdType);
break;
}
pCommand->command = cmdType;
pCommand->sessionId = sessionId;
switch ( cmdType )
{
case eSmeCommandAddTs:
if( pQoSInfo )
{
status = eHAL_STATUS_SUCCESS;
pCommand->u.qosCmd.tspecInfo = *pQoSInfo;
pCommand->u.qosCmd.ac = ac;
}
else
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: NULL pointer passed",
__func__, __LINE__);
status = eHAL_STATUS_INVALID_PARAMETER;
}
break;
case eSmeCommandDelTs:
status = eHAL_STATUS_SUCCESS;
pCommand->u.qosCmd.ac = ac;
pCommand->u.qosCmd.tspec_mask = tspec_mask;
break;
default:
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: invalid command type %d",
__func__, __LINE__, cmdType );
status = eHAL_STATUS_INVALID_PARAMETER;
break;
}
} while( 0 );
if( HAL_STATUS_SUCCESS( status ) && pCommand )
{
smePushCommand( pMac, pCommand, eANI_BOOLEAN_FALSE );
}
else if( pCommand )
{
qosReleaseCommand( pMac, pCommand );
}
return( status );
}
tANI_BOOLEAN qosProcessCommand( tpAniSirGlobal pMac, tSmeCmd *pCommand )
{
eHalStatus status = eHAL_STATUS_SUCCESS;
tANI_BOOLEAN fRemoveCmd = eANI_BOOLEAN_TRUE;
do
{
switch ( pCommand->command )
{
case eSmeCommandAddTs:
status = sme_QosAddTsReq( pMac, (v_U8_t)pCommand->sessionId, &pCommand->u.qosCmd.tspecInfo, pCommand->u.qosCmd.ac);
if( HAL_STATUS_SUCCESS( status ) )
{
fRemoveCmd = eANI_BOOLEAN_FALSE;
status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP;
}
break;
case eSmeCommandDelTs:
status = sme_QosDelTsReq( pMac, (v_U8_t)pCommand->sessionId, pCommand->u.qosCmd.ac, pCommand->u.qosCmd.tspec_mask );
if( HAL_STATUS_SUCCESS( status ) )
{
fRemoveCmd = eANI_BOOLEAN_FALSE;
}
break;
default:
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: invalid command type %d",
__func__, __LINE__, pCommand->command );
break;
}//switch
} while(0);
return( fRemoveCmd );
}
/*
sme_QosTriggerUapsdChange
Invoked by BTC when UAPSD bypass is enabled or disabled
We, in turn, must disable or enable UAPSD on all flows as appropriate
That may require us to re-add TSPECs or to reassociate
*/
sme_QosStatusType sme_QosTriggerUapsdChange( tpAniSirGlobal pMac )
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
v_U8_t ac, tspec1 = 0, tspec2 = 0;
v_U8_t uapsd_mask;
tDot11fBeaconIEs *pIesLocal;
v_U8_t acm_mask;
v_BOOL_t fIsUapsdNeeded;
v_U8_t sessionId;
v_BOOL_t addtsWhenACMNotSet = CSR_IS_ADDTS_WHEN_ACMOFF_SUPPORTED(pMac);
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Invoked",
__func__, __LINE__);
for (sessionId = 0; sessionId < CSR_ROAM_SESSION_MAX; ++sessionId)
{
pSession = &sme_QosCb.sessionInfo[sessionId];
if( !pSession->sessionActive )
{
continue;
}
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Session %d is active",
__func__, __LINE__,
sessionId);
if( HAL_STATUS_SUCCESS(csrGetParsedBssDescriptionIEs(pMac, pSession->assocInfo.pBssDesc, &pIesLocal)) )
{
// get the ACM mask
acm_mask = sme_QosGetACMMask(pMac, pSession->assocInfo.pBssDesc, pIesLocal);
vos_mem_free(pIesLocal);
// get the uapsd mask for this session
uapsd_mask = pSession->apsdMask;
// unmask the bits with ACM on to avoid reassoc on them
uapsd_mask &= ~acm_mask;
// iterate through the ACs to determine if we need to re-add any TSPECs
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
// Does this AC have QoS active?
if( SME_QOS_QOS_ON == pACInfo->curr_state )
{
// Yes, QoS is active on this AC
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: On session %d AC %d has QoS active",
__func__, __LINE__,
sessionId, ac);
// Does this AC require ACM?
if(( acm_mask & (1 << (SME_QOS_EDCA_AC_VO - ac)) ) || addtsWhenACMNotSet )
{
// Yes, so we need to re-add any TSPECS
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: On session %d AC %d has ACM enabled",
__func__, __LINE__,
sessionId, ac);
// Are any TSPECs active?
if( pACInfo->tspec_mask_status )
{
// Yes, at least 1 TSPEC is active. Are they both active?
if( SME_QOS_TSPEC_MASK_BIT_1_2_SET == pACInfo->tspec_mask_status )
{
//both TSPECS are active
tspec1 = SME_QOS_TSPEC_MASK_BIT_1_SET;
tspec2 = SME_QOS_TSPEC_MASK_BIT_2_SET;
}
else
{
// only one TSPEC is active, get its mask
tspec1 = SME_QOS_TSPEC_MASK_BIT_1_2_SET & pACInfo->tspec_mask_status;
}
// Does TSPEC 1 really require UAPSD?
fIsUapsdNeeded = (v_BOOL_t)(pACInfo->curr_QoSInfo[tspec1 - 1].ts_info.psb);
//double check whether we need to do anything
if( fIsUapsdNeeded )
{
pACInfo->requested_QoSInfo[tspec1 - 1] =
pACInfo->curr_QoSInfo[tspec1 - 1];
sme_QosReRequestAddTS( pMac, sessionId,
&pACInfo->requested_QoSInfo[tspec1 - 1],
ac,
tspec1 );
}
// Is TSPEC 2 active?
if( tspec2 )
{
// Does TSPEC 2 really require UAPSD?
fIsUapsdNeeded = (v_BOOL_t)(pACInfo->curr_QoSInfo[tspec2 - 1].ts_info.psb);
if( fIsUapsdNeeded )
{
//No need to inform HDD
//pACInfo->hoRenewal = VOS_TRUE;
pACInfo->requested_QoSInfo[tspec2 - 1] =
pACInfo->curr_QoSInfo[tspec2 - 1];
sme_QosReRequestAddTS( pMac, sessionId,
&pACInfo->requested_QoSInfo[tspec2 - 1],
ac,
tspec2);
}
}
}
else
{
// QoS is set, ACM is on, but no TSPECs -- inconsistent state
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d AC %d has QoS enabled and ACM is set, but no TSPEC",
__func__, __LINE__,
sessionId, ac);
VOS_ASSERT(0);
}
}
else
{
//Since ACM bit is not set, there should be only one QoS information for both directions.
fIsUapsdNeeded = (v_BOOL_t)(pACInfo->curr_QoSInfo[0].ts_info.psb);
if(fIsUapsdNeeded)
{
// we need UAPSD on this AC (and we may not currently have it)
uapsd_mask |= 1 << (SME_QOS_EDCA_AC_VO - ac);
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: On session %d AC %d has ACM disabled, uapsd mask now 0x%X",
__func__, __LINE__,
sessionId, ac, uapsd_mask);
}
}
}
}
// do we need to reassociate?
if(uapsd_mask)
{
tCsrRoamModifyProfileFields modifyProfileFields;
//we need to do a reassoc on these AC
csrGetModifyProfileFields(pMac, sessionId, &modifyProfileFields);
modifyProfileFields.uapsd_mask = uapsd_mask;
//Do we need to inform HDD?
if(!HAL_STATUS_SUCCESS(sme_QosRequestReassoc(pMac, sessionId, &modifyProfileFields, VOS_TRUE)))
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On Session %d Reassoc failed",
__func__, __LINE__,
sessionId);
}
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On Session %d failed to parse IEs",
__func__, __LINE__,
sessionId);
}
}
// return status is ignored by BTC
return SME_QOS_STATUS_SETUP_SUCCESS_IND;
}
/*
sme_QosReRequestAddTS to re-send AddTS for the combined QoS request
*/
static sme_QosStatusType sme_QosReRequestAddTS(tpAniSirGlobal pMac,
v_U8_t sessionId,
sme_QosWmmTspecInfo * pQoSInfo,
sme_QosEdcaAcType ac,
v_U8_t tspecMask)
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
sme_QosStatusType status = SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP;
sme_QosCmdInfo cmd;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Invoked on session %d for AC %d TSPEC %d",
__func__, __LINE__,
sessionId, ac, tspecMask);
pSession = &sme_QosCb.sessionInfo[sessionId];
pACInfo = &pSession->ac_info[ac];
/* Need to vote off power save for the duration of this request */
pSession->readyForPowerSave = VOS_FALSE;
/*
* Check to consider the following flowing scenario Addts request is pending
* on one AC, while APSD requested on another which needs a reassoc.
* Will buffer a request if Addts is pending on any AC, which will safeguard
* the above scenario, & also won't confuse PE with back to back Addts or
* Addts followed by Reassoc.
*/
if(!pMac->psOffloadEnabled)
{
if(sme_QosIsRspPending(sessionId, ac) ||
( eHAL_STATUS_PMC_PENDING == pmcRequestFullPower(pMac,
sme_QosPmcFullPowerCallback, pSession, eSME_REASON_OTHER)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d buffering the AddTS request "
"for AC %d in state %d as Addts is pending "
"on other AC or waiting for full power",
__func__, __LINE__,
sessionId, ac, pACInfo->curr_state);
//buffer cmd
cmd.command = SME_QOS_RESEND_REQ;
cmd.pMac = pMac;
cmd.sessionId = sessionId;
cmd.u.resendCmdInfo.ac = ac;
cmd.u.resendCmdInfo.tspecMask = tspecMask;
cmd.u.resendCmdInfo.QoSInfo = *pQoSInfo;
if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_FALSE)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d unable to buffer the AddTS "
"request for AC %d TSPEC %d in state %d",
__func__, __LINE__,
sessionId, ac, tspecMask, pACInfo->curr_state);
/* Unable to service the request
nothing is pending so vote power save back on */
pSession->readyForPowerSave = VOS_TRUE;
return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP;
}
return SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP;
}
}
else
{
if(sme_QosIsRspPending(sessionId, ac) ||
(eHAL_STATUS_PMC_PENDING == pmcOffloadRequestFullPower(pMac, sessionId,
sme_QosPmcOffloadFullPowerCallback, pSession, eSME_REASON_OTHER)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d buffering the AddTS request "
"for AC %d in state %d as Addts is pending "
"on other AC or waiting for full power",
__func__, __LINE__,
sessionId, ac, pACInfo->curr_state);
//buffer cmd
cmd.command = SME_QOS_RESEND_REQ;
cmd.pMac = pMac;
cmd.sessionId = sessionId;
cmd.u.resendCmdInfo.ac = ac;
cmd.u.resendCmdInfo.tspecMask = tspecMask;
cmd.u.resendCmdInfo.QoSInfo = *pQoSInfo;
if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_FALSE)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: On session %d unable to buffer the AddTS "
"request for AC %d TSPEC %d in state %d",
__func__, __LINE__,
sessionId, ac, tspecMask, pACInfo->curr_state);
/* Unable to service the request
nothing is pending so vote power save back on */
pSession->readyForPowerSave = VOS_TRUE;
return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP;
}
return SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP;
}
}
//get into the stat m/c to see if the request can be granted
switch(pACInfo->curr_state)
{
case SME_QOS_QOS_ON:
{
//if ACM, send out a new ADDTS
pACInfo->hoRenewal = VOS_TRUE;
status = sme_QosSetup(pMac, sessionId, pQoSInfo, ac);
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: sme_QosSetup returned in SME_QOS_QOS_ON state on "
"AC %d with status =%d",
__func__, __LINE__,
ac, status);
if(SME_QOS_STATUS_SETUP_REQ_PENDING_RSP != status)
{
/* We aren't waiting for a response from the AP
so vote power save back on */
pSession->readyForPowerSave = VOS_TRUE;
}
if(SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status)
{
status = SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP;
pACInfo->tspec_pending = tspecMask;
}
else if((SME_QOS_STATUS_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) ||
(SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status) ||
(SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_PENDING == status))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: UAPSD is setup already status = %d "
"returned by sme_QosSetup",
__func__, __LINE__,
status);
}
else
{
//err msg
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: unexpected status = %d returned by sme_QosSetup",
__func__, __LINE__,
status);
}
}
break;
case SME_QOS_HANDOFF:
case SME_QOS_REQUESTED:
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: Re-Add request in state = %d buffer the request",
__func__, __LINE__,
pACInfo->curr_state);
cmd.command = SME_QOS_RESEND_REQ;
cmd.pMac = pMac;
cmd.sessionId = sessionId;
cmd.u.resendCmdInfo.ac = ac;
cmd.u.resendCmdInfo.tspecMask = tspecMask;
cmd.u.resendCmdInfo.QoSInfo = *pQoSInfo;
if(!HAL_STATUS_SUCCESS(sme_QosBufferCmd(&cmd, VOS_FALSE)))
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: couldn't buffer the read request in state = %d",
__func__, __LINE__,
pACInfo->curr_state );
/* Unable to service the request
nothing is pending so vote power save back on */
pSession->readyForPowerSave = VOS_TRUE;
return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP;
}
status = SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP;
break;
case SME_QOS_CLOSED:
case SME_QOS_INIT:
case SME_QOS_LINK_UP:
default:
//print error msg,
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: %d: ReAdd request in unexpected state = %d",
__func__, __LINE__,
pACInfo->curr_state );
/* Unable to service the request
nothing is pending so vote power save back on */
pSession->readyForPowerSave = VOS_TRUE;
// ASSERT?
break;
}
if((SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_NO_ACM_NO_APSD_RSP == status) ||
(SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_APSD_SET_ALREADY == status))
{
(void)sme_QosProcessBufferedCmd(sessionId);
}
return (status);
}
static void sme_QosInitACs(tpAniSirGlobal pMac, v_U8_t sessionId)
{
sme_QosSessionInfo *pSession;
sme_QosEdcaAcType ac;
pSession = &sme_QosCb.sessionInfo[sessionId];
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
vos_mem_zero(&pSession->ac_info[ac], sizeof(sme_QosACInfo));
sme_QosStateTransition(sessionId, ac, SME_QOS_INIT);
}
}
static eHalStatus sme_QosRequestReassoc(tpAniSirGlobal pMac, tANI_U8 sessionId,
tCsrRoamModifyProfileFields *pModFields,
v_BOOL_t fForce )
{
sme_QosSessionInfo *pSession;
sme_QosACInfo *pACInfo;
eHalStatus status;
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: %d: Invoked on session %d with UAPSD mask 0x%X",
__func__, __LINE__,
sessionId, pModFields->uapsd_mask);
pSession = &sme_QosCb.sessionInfo[sessionId];
status = csrReassoc(pMac, sessionId, pModFields, &pSession->roamID, fForce);
if(HAL_STATUS_SUCCESS(status))
{
//Update the state to Handoff so subsequent requests are queued until
// this one is finished
sme_QosEdcaAcType ac;
for(ac = SME_QOS_EDCA_AC_BE; ac < SME_QOS_EDCA_AC_MAX; ac++)
{
pACInfo = &pSession->ac_info[ac];
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO,
"%s: %d: AC[%d] is in state [%d]",
__func__, __LINE__,
ac, pACInfo->curr_state );
// If it is already in HANDOFF state, don't do anything since we
// MUST preserve the previous state and sme_QosStateTransition
// will change the previous state
if(SME_QOS_HANDOFF != pACInfo->curr_state)
{
sme_QosStateTransition(sessionId, ac, SME_QOS_HANDOFF);
}
}
}
return status;
}
static v_U32_t sme_QosAssignFlowId(void)
{
v_U32_t flowId;
flowId = sme_QosCb.nextFlowId;
if (SME_QOS_MAX_FLOW_ID == flowId)
{
// The Flow ID wrapped. This is obviously not a real life scenario
// but handle it to keep the software test folks happy
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,
"%s: %d: Software Test made the flow counter wrap, "
"QoS may no longer be functional",
__func__, __LINE__);
sme_QosCb.nextFlowId = SME_QOS_MIN_FLOW_ID;
}
else
{
sme_QosCb.nextFlowId++;
}
return flowId;
}
static v_U8_t sme_QosAssignDialogToken(void)
{
v_U8_t token;
token = sme_QosCb.nextDialogToken;
if (SME_QOS_MAX_DIALOG_TOKEN == token)
{
// wrap is ok
sme_QosCb.nextDialogToken = SME_QOS_MIN_DIALOG_TOKEN;
}
else
{
sme_QosCb.nextDialogToken++;
}
return token;
}
#endif /* WLAN_MDM_CODE_REDUCTION_OPT */