/*
 * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
 *
 * 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.
 */

/*
 * DOC: sme_qos.c
 *
 *  Implementation for SME QoS APIs
 */
/* $Header$ */
/* Include Files */

#include "ani_global.h"

#include "sme_inside.h"
#include "csr_inside_api.h"
#include "host_diag_core_event.h"
#include "host_diag_core_log.h"

#include "utils_parser.h"
#include "sme_power_save_api.h"
#include "wlan_mlme_ucfg_api.h"
#include "wlan_cm_roam_api.h"

#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 flowlist (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    WLAN_MAX_VDEVS
#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

#ifdef WLAN_FEATURE_MSCS
#define MSCS_USER_PRIORITY               0x07C0
#define MSCS_STREAM_TIMEOUT              60 /* in sec */
#define MSCS_TCLAS_CLASSIFIER_MASK       0x5F
#define MSCS_TCLAS_CLASSIFIER_TYPE       4
#endif

/* Type declarations */
/* Enumeration of the various states in the QoS state m/c */
enum sme_qos_states {
	SME_QOS_CLOSED = 0,
	SME_QOS_INIT,
	SME_QOS_LINK_UP,
	SME_QOS_REQUESTED,
	SME_QOS_QOS_ON,
	SME_QOS_HANDOFF,

};
/* Enumeration of the various Release QoS trigger */
enum sme_qosrel_triggers {
	SME_QOS_RELEASE_DEFAULT = 0,
	SME_QOS_RELEASE_BY_AP,
};
/* Enumeration of the various QoS cmds */
enum sme_qos_cmdtype {
	SME_QOS_SETUP_REQ = 0,
	SME_QOS_RELEASE_REQ,
	SME_QOS_MODIFY_REQ,
	SME_QOS_RESEND_REQ,
	SME_QOS_CMD_MAX
};
/* Enumeration of the various QoS reason codes to be used in the Flow list */
enum sme_qos_reasontype {
	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
};

/* Table to map user priority passed in as an argument to appropriate Access
 * Category as specified in 802.11e/WMM
 */
enum qca_wlan_ac_type sme_qos_up_to_ac_map[SME_QOS_WMM_UP_MAX] = {
	QCA_WLAN_AC_BE,     /* User Priority 0 */
	QCA_WLAN_AC_BK,     /* User Priority 1 */
	QCA_WLAN_AC_BK,     /* User Priority 2 */
	QCA_WLAN_AC_BE,     /* User Priority 3 */
	QCA_WLAN_AC_VI,     /* User Priority 4 */
	QCA_WLAN_AC_VI,     /* User Priority 5 */
	QCA_WLAN_AC_VO,     /* User Priority 6 */
	QCA_WLAN_AC_VO      /* User Priority 7 */
};

/*
 * 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
 */
struct sme_qos_flowinfoentry {
	tListElem link;         /* list links */
	uint8_t sessionId;
	uint8_t tspec_mask;
	enum sme_qos_reasontype reason;
	uint32_t QosFlowID;
	enum qca_wlan_ac_type ac_type;
	struct sme_qos_wmmtspecinfo QoSInfo;
	void *HDDcontext;
	sme_QosCallback QoSCallback;
	bool hoRenewal;       /* 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 */
};
/*
 *  DESCRIPTION
 *  SME QoS module's setup request cmd related information structure.
 */
struct sme_qos_setupcmdinfo {
	uint32_t QosFlowID;
	struct sme_qos_wmmtspecinfo QoSInfo;
	void *HDDcontext;
	sme_QosCallback QoSCallback;
	enum sme_qos_wmmuptype UPType;
	bool hoRenewal;       /* 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 */
};
/*
 *  DESCRIPTION
 *  SME QoS module's modify cmd related information structure.
 */
struct sme_qos_modifycmdinfo {
	uint32_t QosFlowID;
	enum qca_wlan_ac_type ac;
	struct sme_qos_wmmtspecinfo QoSInfo;
};
/*
 *  DESCRIPTION
 *  SME QoS module's resend cmd related information structure.
 */
struct sme_qos_resendcmdinfo {
	uint8_t tspecMask;
	enum qca_wlan_ac_type ac;
	struct sme_qos_wmmtspecinfo QoSInfo;
};
/*
 *  DESCRIPTION
 *  SME QoS module's release cmd related information structure.
 */
struct sme_qos_releasecmdinfo {
	uint32_t QosFlowID;
};
/*
 *  DESCRIPTION
 *  SME QoS module's buffered cmd related information structure.
 */
struct sme_qos_cmdinfo {
	enum sme_qos_cmdtype command;
	struct mac_context *mac;
	uint8_t sessionId;
	union {
		struct sme_qos_setupcmdinfo setupCmdInfo;
		struct sme_qos_modifycmdinfo modifyCmdInfo;
		struct sme_qos_resendcmdinfo resendCmdInfo;
		struct sme_qos_releasecmdinfo releaseCmdInfo;
	} u;
};
/*
 *  DESCRIPTION
 *  SME QoS module's buffered cmd List structure. This list can hold information
 *  related to any pending cmd from HDD
 */
struct sme_qos_cmdinfoentry {
	tListElem link;         /* list links */
	struct sme_qos_cmdinfo cmdInfo;
};
/*
 *  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
 */
struct sme_qos_acinfo {
	uint8_t num_flows[SME_QOS_TSPEC_INDEX_MAX];
	enum sme_qos_states curr_state;
	enum sme_qos_states prev_state;
	struct sme_qos_wmmtspecinfo curr_QoSInfo[SME_QOS_TSPEC_INDEX_MAX];
	struct sme_qos_wmmtspecinfo requested_QoSInfo[SME_QOS_TSPEC_INDEX_MAX];
	/* reassoc requested for APSD */
	bool reassoc_pending;
	/*
	 * 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
	 */
	/* status showing if both the indices are in use */
	uint8_t tspec_mask_status;
	/* tspec negotiation going on for which index */
	uint8_t tspec_pending;
	/* set to true while re-negotiating flows after */
	bool hoRenewal;
	/*
	 * 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
	 */
	uint8_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];
	enum sme_qosrel_triggers relTrig;

};
/*
 *  DESCRIPTION
 *  SME QoS module's Per session information structure. This can hold
 *  information on the state of the session
 */
struct sme_qos_sessioninfo {
	/* what is this entry's session id */
	uint8_t sessionId;
	/* is the session currently active */
	bool sessionActive;
	/* All AC info for this session */
	struct sme_qos_acinfo ac_info[QCA_WLAN_AC_ALL];
	/* Bitmask of the ACs with APSD on */
	/* Bit0:VO; Bit1:VI; Bit2:BK; Bit3:BE all other bits are ignored */
	uint8_t apsdMask;
	/* association information for this session */
	sme_QosAssocInfo assocInfo;
	/* ID assigned to our reassoc request */
	uint32_t roamID;
	/* are we in the process of handing off to a different AP */
	bool handoffRequested;
	/* commands that are being buffered for this session */
	tDblLinkList bufferedCommandList;

	bool ftHandoffInProgress;

};
/*
 *  DESCRIPTION
 *  Search key union. We can use the flowID, ac type, or reason to find an
 *  entry in the flow list
 */
union sme_qos_searchkey {
	uint32_t QosFlowID;
	enum qca_wlan_ac_type ac_type;
	enum sme_qos_reasontype reason;
};
/*
 *  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
 */
struct sme_qos_searchinfo {
	uint8_t sessionId;
	uint8_t index;
	union sme_qos_searchkey key;
	enum sme_qos_wmm_dir_type direction;
	uint8_t tspec_mask;
};

typedef QDF_STATUS (*sme_QosProcessSearchEntry)(struct mac_context *mac,
						tListElem *pEntry);

static enum sme_qos_statustype sme_qos_internal_setup_req(struct mac_context *mac,
					  uint8_t sessionId,
					  struct sme_qos_wmmtspecinfo *pQoSInfo,
					  sme_QosCallback QoSCallback,
					  void *HDDcontext,
					  enum sme_qos_wmmuptype UPType,
					  uint32_t QosFlowID,
					  bool buffered_cmd, bool hoRenewal);
static enum sme_qos_statustype sme_qos_internal_modify_req(struct mac_context *mac,
					  struct sme_qos_wmmtspecinfo *pQoSInfo,
					  uint32_t QosFlowID,
					  bool buffered_cmd);
static enum sme_qos_statustype sme_qos_internal_release_req(struct mac_context *mac,
					       uint8_t session_id,
					       uint32_t QosFlowID,
					       bool buffered_cmd);
static enum sme_qos_statustype sme_qos_setup(struct mac_context *mac,
				uint8_t sessionId,
				struct sme_qos_wmmtspecinfo *pTspec_Info,
				enum qca_wlan_ac_type ac);
static QDF_STATUS sme_qos_add_ts_req(struct mac_context *mac,
			      uint8_t sessionId,
			      struct sme_qos_wmmtspecinfo *pTspec_Info,
			      enum qca_wlan_ac_type ac);
static QDF_STATUS sme_qos_del_ts_req(struct mac_context *mac,
			      uint8_t sessionId,
			      enum qca_wlan_ac_type ac, uint8_t tspec_mask);
static QDF_STATUS sme_qos_process_add_ts_rsp(struct mac_context *mac,
						void *msg_buf);
static QDF_STATUS sme_qos_process_del_ts_ind(struct mac_context *mac,
						void *msg_buf);
static QDF_STATUS sme_qos_process_del_ts_rsp(struct mac_context *mac,
						void *msg_buf);
static QDF_STATUS sme_qos_process_assoc_complete_ev(struct mac_context *mac,
					uint8_t sessionId, void *pEvent_info);
static QDF_STATUS sme_qos_process_reassoc_req_ev(struct mac_context *mac,
					uint8_t sessionId, void *pEvent_info);
static QDF_STATUS sme_qos_process_reassoc_success_ev(struct mac_context *mac,
					  uint8_t sessionId, void *pEvent_info);
static QDF_STATUS sme_qos_process_reassoc_failure_ev(struct mac_context *mac,
					  uint8_t sessionId, void *pEvent_info);
static QDF_STATUS sme_qos_process_disconnect_ev(struct mac_context *mac, uint8_t
					sessionId, void *pEvent_info);
static QDF_STATUS sme_qos_process_join_req_ev(struct mac_context *mac, uint8_t
						sessionId, void *pEvent_info);
static QDF_STATUS sme_qos_process_handoff_assoc_req_ev(struct mac_context *mac,
						uint8_t sessionId,
						void *pEvent_info);
static QDF_STATUS sme_qos_process_handoff_success_ev(struct mac_context *mac,
					  uint8_t sessionId, void *pEvent_info);
static QDF_STATUS sme_qos_process_preauth_success_ind(struct mac_context *mac,
					       uint8_t sessionId,
					       void *pEvent_info);
static QDF_STATUS sme_qos_process_set_key_success_ind(struct mac_context *mac,
					       uint8_t sessionId,
						void *pEvent_info);
static QDF_STATUS sme_qos_process_aggr_qos_rsp(struct mac_context *mac,
						void *msg_buf);
static QDF_STATUS sme_qos_ft_aggr_qos_req(struct mac_context *mac, uint8_t
					sessionId);
static QDF_STATUS sme_qos_process_add_ts_success_rsp(struct mac_context *mac,
					      uint8_t sessionId,
					      tSirAddtsRspInfo *pRsp);
static QDF_STATUS sme_qos_process_add_ts_failure_rsp(struct mac_context *mac,
					      uint8_t sessionId,
					      tSirAddtsRspInfo *pRsp);
static QDF_STATUS sme_qos_aggregate_params(
	struct sme_qos_wmmtspecinfo *pInput_Tspec_Info,
	struct sme_qos_wmmtspecinfo *pCurrent_Tspec_Info,
	struct sme_qos_wmmtspecinfo *pUpdated_Tspec_Info);
static QDF_STATUS sme_qos_update_params(uint8_t sessionId,
				      enum qca_wlan_ac_type ac,
				      uint8_t tspec_mask,
				      struct sme_qos_wmmtspecinfo *pTspec_Info);
static enum qca_wlan_ac_type sme_qos_up_to_ac(enum sme_qos_wmmuptype up);

static bool
sme_qos_is_acm(struct mac_context *mac, struct bss_description *pSirBssDesc,
	       enum qca_wlan_ac_type ac, tDot11fBeaconIEs *pIes);

static tListElem *sme_qos_find_in_flow_list(struct sme_qos_searchinfo
						search_key);
static QDF_STATUS sme_qos_find_all_in_flow_list(struct mac_context *mac,
					 struct sme_qos_searchinfo search_key,
					 sme_QosProcessSearchEntry fnp);
static void sme_qos_state_transition(uint8_t sessionId,
				     enum qca_wlan_ac_type ac,
				     enum sme_qos_states new_state);
static QDF_STATUS sme_qos_buffer_cmd(struct sme_qos_cmdinfo *pcmd, bool
					insert_head);
static QDF_STATUS sme_qos_process_buffered_cmd(uint8_t sessionId);
static QDF_STATUS sme_qos_save_assoc_info(struct sme_qos_sessioninfo *pSession,
				   sme_QosAssocInfo *pAssoc_info);
static QDF_STATUS sme_qos_setup_fnp(struct mac_context *mac, tListElem *pEntry);
static QDF_STATUS sme_qos_modification_notify_fnp(struct mac_context *mac,
					   tListElem *pEntry);
static QDF_STATUS sme_qos_modify_fnp(struct mac_context *mac, tListElem *pEntry);
static QDF_STATUS sme_qos_del_ts_ind_fnp(struct mac_context *mac, tListElem
					*pEntry);
static QDF_STATUS sme_qos_reassoc_success_ev_fnp(struct mac_context *mac,
					tListElem *pEntry);
static QDF_STATUS sme_qos_add_ts_failure_fnp(struct mac_context *mac, tListElem
						*pEntry);
static QDF_STATUS sme_qos_add_ts_success_fnp(struct mac_context *mac, tListElem
						*pEntry);
static bool sme_qos_is_rsp_pending(uint8_t sessionId, enum qca_wlan_ac_type ac);
static bool sme_qos_is_uapsd_active(void);

static QDF_STATUS sme_qos_buffer_existing_flows(struct mac_context *mac,
						uint8_t sessionId);
static QDF_STATUS sme_qos_delete_existing_flows(struct mac_context *mac,
						uint8_t sessionId);
static void sme_qos_cleanup_ctrl_blk_for_handoff(struct mac_context *mac,
						 uint8_t sessionId);
static QDF_STATUS sme_qos_delete_buffered_requests(struct mac_context *mac,
						   uint8_t sessionId);
static bool sme_qos_validate_requested_params(struct mac_context *mac,
				       struct sme_qos_wmmtspecinfo *pQoSInfo,
				       uint8_t sessionId);

static QDF_STATUS qos_issue_command(struct mac_context *mac, uint8_t sessionId,
				    eSmeCommandType cmdType,
				    struct sme_qos_wmmtspecinfo *pQoSInfo,
				    enum qca_wlan_ac_type ac, uint8_t tspec_mask);
/* sme_qos_re_request_add_ts to re-send AddTS for the combined QoS request */
static enum sme_qos_statustype sme_qos_re_request_add_ts(struct mac_context *mac,
					  uint8_t sessionId,
					  struct sme_qos_wmmtspecinfo *pQoSInfo,
					  enum qca_wlan_ac_type ac,
					  uint8_t tspecMask);
static void sme_qos_init_a_cs(struct mac_context *mac, uint8_t sessionId);
static QDF_STATUS sme_qos_request_reassoc(struct mac_context *mac,
					uint8_t sessionId,
					  tCsrRoamModifyProfileFields *
					  pModFields, bool fForce);
static uint32_t sme_qos_assign_flow_id(void);
static uint8_t sme_qos_assign_dialog_token(void);
static QDF_STATUS sme_qos_update_tspec_mask(uint8_t sessionId,
					   struct sme_qos_searchinfo search_key,
					    uint8_t new_tspec_mask);

/*
 *  DESCRIPTION
 *  SME QoS module's internal control block.
 */
struct sme_qos_cb_s {
	/* global Mac pointer */
	struct mac_context *mac;
	/* All Session Info */
	struct sme_qos_sessioninfo *sessionInfo;
	/* All FLOW info */
	tDblLinkList flow_list;
	/* default TSPEC params */
	struct sme_qos_wmmtspecinfo *def_QoSInfo;
	/* counter for assigning Flow IDs */
	uint32_t nextFlowId;
	/* counter for assigning Dialog Tokens */
	uint8_t nextDialogToken;
} sme_qos_cb;

#ifdef WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY
static inline QDF_STATUS sme_qos_allocate_control_block_buffer(void)
{
	uint32_t buf_size;

	buf_size = WLAN_MAX_VDEVS * sizeof(struct sme_qos_sessioninfo);
	sme_qos_cb.sessionInfo = qdf_mem_malloc(buf_size);
	if (!sme_qos_cb.sessionInfo)
		return QDF_STATUS_E_NOMEM;

	buf_size = QCA_WLAN_AC_ALL * sizeof(struct sme_qos_wmmtspecinfo);
	sme_qos_cb.def_QoSInfo = qdf_mem_malloc(buf_size);

	if (!sme_qos_cb.def_QoSInfo) {
		qdf_mem_free(sme_qos_cb.sessionInfo);
		return QDF_STATUS_E_NOMEM;
	}

	return QDF_STATUS_SUCCESS;
}

static inline void sme_qos_free_control_block_buffer(void)
{
	qdf_mem_free(sme_qos_cb.sessionInfo);
	sme_qos_cb.sessionInfo = NULL;

	qdf_mem_free(sme_qos_cb.def_QoSInfo);
	sme_qos_cb.def_QoSInfo = NULL;
}

#else /* WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY */

struct sme_qos_sessioninfo sessionInfo[WLAN_MAX_VDEVS];
struct sme_qos_wmmtspecinfo def_QoSInfo[QCA_WLAN_AC_ALL];

static inline QDF_STATUS sme_qos_allocate_control_block_buffer(void)
{
	qdf_mem_zero(&sessionInfo, sizeof(sessionInfo));
	sme_qos_cb.sessionInfo = sessionInfo;
	qdf_mem_zero(&def_QoSInfo, sizeof(def_QoSInfo));
	sme_qos_cb.def_QoSInfo = def_QoSInfo;

	return QDF_STATUS_SUCCESS;
}

static inline void sme_qos_free_control_block_buffer(void)
{
	sme_qos_cb.sessionInfo = NULL;
	sme_qos_cb.def_QoSInfo = NULL;
}
#endif /* WLAN_ALLOCATE_GLOBAL_BUFFERS_DYNAMICALLY */

/* External APIs definitions */

/**
 * sme_qos_open() - called to initialize SME QoS module.
 * @mac: global MAC context
 *
 * This function must be called before any API call to
 * SME QoS module.
 *
 * Return: QDF_STATUS
 */
QDF_STATUS sme_qos_open(struct mac_context *mac)
{
	struct sme_qos_sessioninfo *pSession;
	uint8_t sessionId;
	QDF_STATUS status;

	sme_debug("Initializing SME-QoS module");
	/* alloc and init the control block */
	/* (note that this will make all sessions invalid) */
	if (!QDF_IS_STATUS_SUCCESS(sme_qos_allocate_control_block_buffer())) {
		sme_err("Failed to allocate buffer");
		return QDF_STATUS_E_NOMEM;
	}
	sme_qos_cb.mac = mac;
	sme_qos_cb.nextFlowId = SME_QOS_MIN_FLOW_ID;
	sme_qos_cb.nextDialogToken = SME_QOS_MIN_DIALOG_TOKEN;

	/* init flow list */
	status = csr_ll_open(&sme_qos_cb.flow_list);
	if (!QDF_IS_STATUS_SUCCESS(status)) {
		sme_err("cannot initialize Flow List");
		sme_qos_free_control_block_buffer();
		return QDF_STATUS_E_FAILURE;
	}

	for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; ++sessionId) {
		pSession = &sme_qos_cb.sessionInfo[sessionId];
		pSession->sessionId = sessionId;
		/* initialize the session's per-AC information */
		sme_qos_init_a_cs(mac, sessionId);
		/* initialize the session's buffered command list */
		status = csr_ll_open(&pSession->bufferedCommandList);
		if (!QDF_IS_STATUS_SUCCESS(status)) {
			sme_err("cannot initialize cmd list for session %d",
				sessionId);
			sme_qos_free_control_block_buffer();
			return QDF_STATUS_E_FAILURE;
		}
	}

	sme_debug("done initializing SME-QoS module");
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_close() - 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_qos_open.
 *
 * mac - Pointer to the global MAC parameter structure.
 * Return QDF_STATUS
 */
QDF_STATUS sme_qos_close(struct mac_context *mac)
{
	struct sme_qos_sessioninfo *pSession;
	enum qca_wlan_ac_type ac;
	uint8_t sessionId;

	sme_debug("closing down SME-QoS");

	/* cleanup control block */
	/* close the flow list */
	csr_ll_close(&sme_qos_cb.flow_list);
	/* shut down all of the sessions */
	for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; ++sessionId) {
		pSession = &sme_qos_cb.sessionInfo[sessionId];
		if (!pSession)
			continue;

		sme_qos_init_a_cs(mac, sessionId);
		/* this session doesn't require UAPSD */
		pSession->apsdMask = 0;

		pSession->handoffRequested = false;
		pSession->roamID = 0;
		/* need to clean up buffered req */
		sme_qos_delete_buffered_requests(mac, sessionId);
		/* need to clean up flows */
		sme_qos_delete_existing_flows(mac, sessionId);

		/* Clean up the assoc info if already allocated */
		if (pSession->assocInfo.bss_desc) {
			qdf_mem_free(pSession->assocInfo.bss_desc);
			pSession->assocInfo.bss_desc = NULL;
		}
		/* close the session's buffered command list */
		csr_ll_close(&pSession->bufferedCommandList);
		for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++)
			sme_qos_state_transition(sessionId, ac, SME_QOS_CLOSED);

		pSession->sessionActive = false;
	}
	sme_qos_free_control_block_buffer();
	sme_debug("closed down QoS");
	return QDF_STATUS_SUCCESS;
}

/**
 * sme_qos_setup_req() - The SME QoS API exposed to HDD to request for QoS
 *                       on a particular AC.
 * @mac_handle: The handle returned by mac_open.
 * @sessionId: sessionId returned by sme_open_session.
 * @pQoSInfo: Pointer to struct sme_qos_wmmtspecinfo which contains the
 *             WMM TSPEC related info as defined above, provided by HDD
 * @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
 * @HDDcontext: A cookie passed by HDD to be used by SME during any QoS
 *              notification (through the callback) to HDD
 * @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
 * @pQosFlowID: Identification per flow running on each AC generated by
 *              SME. It is only meaningful if the QoS setup for the flow is
 *              successful
 * 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.
 * Return: QDF_STATUS_SUCCESS - Setup is successful.
 *          Other status means Setup request failed
 */
enum sme_qos_statustype sme_qos_setup_req(mac_handle_t mac_handle,
					  uint32_t sessionId,
					  struct sme_qos_wmmtspecinfo *pQoSInfo,
					  sme_QosCallback QoSCallback,
					  void *HDDcontext,
					  enum sme_qos_wmmuptype UPType,
					  uint32_t *pQosFlowID)
{
	struct sme_qos_sessioninfo *pSession;
	QDF_STATUS lock_status = QDF_STATUS_E_FAILURE;
	struct mac_context *mac = MAC_CONTEXT(mac_handle);
	enum sme_qos_statustype status;

	sme_debug("QoS Setup requested by client on session %d", sessionId);
	lock_status = sme_acquire_global_lock(&mac->sme);
	if (!QDF_IS_STATUS_SUCCESS(lock_status))
		return SME_QOS_STATUS_SETUP_FAILURE_RSP;
	/* Make sure the session is valid */
	if (!CSR_IS_SESSION_VALID(mac, sessionId)) {
		sme_err("Supplied Session ID %d is invalid", sessionId);
		status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
	} else {
		/* Make sure the session is active */
		pSession = &sme_qos_cb.sessionInfo[sessionId];
		if (!pSession->sessionActive) {
			sme_err("Supplied Session ID %d is inactive",
				sessionId);
			status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
		} else {
			/* Assign a Flow ID */
			*pQosFlowID = sme_qos_assign_flow_id();
			sme_debug("QoS request on session %d assigned Flow ID %d",
				  sessionId, *pQosFlowID);
			/* Call the internal function for QoS setup, */
			/* adding a layer of abstraction */
			status =
				sme_qos_internal_setup_req(mac, (uint8_t)
							sessionId,
							  pQoSInfo, QoSCallback,
							   HDDcontext, UPType,
							   *pQosFlowID, false,
							   false);
		}
	}
	sme_release_global_lock(&mac->sme);
	sme_debug("QoS setup return status on session %d is %d",
		  sessionId, status);
	return status;
}

/**
 * sme_qos_modify_req() - The SME QoS API exposed to HDD to request for
 *  modification of certain QoS params on a flow running on a particular AC.
 * @mac_handle: The handle returned by mac_open.
 * @pQoSInfo: Pointer to struct sme_qos_wmmtspecinfo which contains the
 *             WMM TSPEC related info as defined above, provided by HDD
 * @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
 *
 * 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.
 *
 * Return: SME_QOS_STATUS_SETUP_SUCCESS_RSP - Modification is successful.
 *         Other status means request failed
 */
enum sme_qos_statustype sme_qos_modify_req(mac_handle_t mac_handle,
					struct sme_qos_wmmtspecinfo *pQoSInfo,
					uint32_t QosFlowID)
{
	QDF_STATUS lock_status = QDF_STATUS_E_FAILURE;
	struct mac_context *mac = MAC_CONTEXT(mac_handle);
	enum sme_qos_statustype status;

	sme_debug("QoS Modify requested by client for Flow %d", QosFlowID);
	lock_status = sme_acquire_global_lock(&mac->sme);
	if (!QDF_IS_STATUS_SUCCESS(lock_status)) {
		sme_err("Unable to obtain lock");
		return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP;
	}
	/* Call the internal function for QoS modify, adding a
	 * layer of abstraction
	 */
	status = sme_qos_internal_modify_req(mac, pQoSInfo, QosFlowID, false);
	sme_release_global_lock(&mac->sme);
	sme_debug("QoS Modify return status on Flow %d is %d",
		  QosFlowID, status);
	return status;
}

/**
 * sme_qos_release_req() - The SME QoS API exposed to HDD to request for
 *                         releasing a QoS flow running on a particular AC.
 *
 * @mac_handle: The handle returned by mac_open.
 * @session_id: session_id returned by sme_open_session.
 * @QosFlowID: Identification per flow running on each AC generated by SME
 *             It is only meaningful if the QoS setup for the flow is successful
 *
 * 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
 *
 * Return: QDF_STATUS_SUCCESS - Release is successful.
 */
enum sme_qos_statustype sme_qos_release_req(mac_handle_t mac_handle,
					    uint8_t session_id,
					    uint32_t QosFlowID)
{
	QDF_STATUS lock_status = QDF_STATUS_E_FAILURE;
	struct mac_context *mac = MAC_CONTEXT(mac_handle);
	enum sme_qos_statustype status;

	sme_debug("QoS Release requested by client for Flow %d", QosFlowID);
	lock_status = sme_acquire_global_lock(&mac->sme);
	if (!QDF_IS_STATUS_SUCCESS(lock_status)) {
		sme_err("Unable to obtain lock");
		return SME_QOS_STATUS_RELEASE_FAILURE_RSP;
	}
	/* Call the internal function for QoS release, adding a
	 * layer of abstraction
	 */
	status = sme_qos_internal_release_req(mac, session_id, QosFlowID,
					      false);
	sme_release_global_lock(&mac->sme);
	sme_debug("QoS Release return status on Flow %d is %d",
		  QosFlowID, status);
	return status;
}

void qos_release_command(struct mac_context *mac, tSmeCmd *pCommand)
{
	qdf_mem_zero(&pCommand->u.qosCmd, sizeof(tGenericQosCmd));
	csr_release_command(mac, pCommand);
}

/**
 * sme_qos_msg_processor() - Processes QOS messages
 * @mac_ctx: Pointer to the global MAC parameter structure.
 * @msg_type: the type of msg passed by PE as defined in wni_api.h
 * @msg: a pointer to a buffer that maps to various structures bases.
 *
 * sme_process_msg() calls this function for the messages that
 * are handled by SME QoS module.
 *
 * Return: QDF_STATUS enumeration.
 */
QDF_STATUS sme_qos_msg_processor(struct mac_context *mac_ctx,
	uint16_t msg_type, void *msg)
{
	QDF_STATUS status = QDF_STATUS_E_FAILURE;
	tListElem *entry = NULL;
	tSmeCmd *command;

	sme_debug("msg = %d for QoS", msg_type);
	/* switch on the msg type & make the state transition accordingly */
	switch (msg_type) {
	case eWNI_SME_ADDTS_RSP:
		entry = csr_nonscan_active_ll_peek_head(mac_ctx,
				LL_ACCESS_LOCK);
		if (!entry)
			break;
		command = GET_BASE_ADDR(entry, tSmeCmd, Link);
		if (eSmeCommandAddTs == command->command) {
			status = sme_qos_process_add_ts_rsp(mac_ctx, msg);
			if (csr_nonscan_active_ll_remove_entry(mac_ctx, entry,
					LL_ACCESS_LOCK)) {
				qos_release_command(mac_ctx, command);
			}
		}
		break;
	case eWNI_SME_DELTS_RSP:
		entry = csr_nonscan_active_ll_peek_head(mac_ctx,
				LL_ACCESS_LOCK);
		if (!entry)
			break;
		command = GET_BASE_ADDR(entry, tSmeCmd, Link);
		if (eSmeCommandDelTs == command->command) {
			status = sme_qos_process_del_ts_rsp(mac_ctx, msg);
			if (csr_nonscan_active_ll_remove_entry(mac_ctx, entry,
					LL_ACCESS_LOCK)) {
				qos_release_command(mac_ctx, command);
			}
		}
		break;
	case eWNI_SME_DELTS_IND:
		status = sme_qos_process_del_ts_ind(mac_ctx, msg);
		break;
	case eWNI_SME_FT_AGGR_QOS_RSP:
		status = sme_qos_process_aggr_qos_rsp(mac_ctx, msg);
		break;
	default:
		/* err msg */
		sme_err("unknown msg type = %d", msg_type);
		break;
	}
	return status;
}

/**
 * sme_qos_process_disconnect_roam_ind() - Delete the existing TSPEC
 * flows when roaming due to disconnect is complete.
 * @mac - Pointer to the global MAC parameter structure.
 * @vdev_id: Vdev id
 *
 * Return: None
 */
static void
sme_qos_process_disconnect_roam_ind(struct mac_context *mac,
				    uint8_t vdev_id)
{
	sme_qos_delete_existing_flows(mac, vdev_id);
}

/*
 * sme_qos_csr_event_ind() - The QoS sub-module in SME expects notifications
 * from CSR when certain events occur as mentioned in sme_qos_csr_event_indType.
 *
 * mac - Pointer to the global MAC parameter structure.
 * ind - The event occurred of type sme_qos_csr_event_indType.
 * pEvent_info - Information related to the event
 * Return QDF_STATUS
 */
QDF_STATUS sme_qos_csr_event_ind(struct mac_context *mac,
				 uint8_t sessionId,
			sme_qos_csr_event_indType ind, void *pEvent_info)
{
	QDF_STATUS status = QDF_STATUS_E_FAILURE;

	sme_debug("On Session %d Event %d received from CSR", sessionId, ind);
	switch (ind) {
	case SME_QOS_CSR_ASSOC_COMPLETE:
		/* expecting assoc info in pEvent_info */
		status = sme_qos_process_assoc_complete_ev(mac, sessionId,
							pEvent_info);
		break;
	case SME_QOS_CSR_REASSOC_REQ:
		/* nothing expected in pEvent_info */
		status = sme_qos_process_reassoc_req_ev(mac, sessionId,
							pEvent_info);
		break;
	case SME_QOS_CSR_REASSOC_COMPLETE:
		/* expecting assoc info in pEvent_info */
		status =
			sme_qos_process_reassoc_success_ev(mac, sessionId,
							   pEvent_info);
		break;
	case SME_QOS_CSR_REASSOC_FAILURE:
		/* nothing expected in pEvent_info */
		status =
			sme_qos_process_reassoc_failure_ev(mac, sessionId,
							   pEvent_info);
		break;
	case SME_QOS_CSR_DISCONNECT_REQ:
	case SME_QOS_CSR_DISCONNECT_IND:
		/* nothing expected in pEvent_info */
		status = sme_qos_process_disconnect_ev(mac, sessionId,
							pEvent_info);
		break;
	case SME_QOS_CSR_JOIN_REQ:
		/* nothing expected in pEvent_info */
		status = sme_qos_process_join_req_ev(mac, sessionId,
							pEvent_info);
		break;
	case SME_QOS_CSR_HANDOFF_ASSOC_REQ:
		/* nothing expected in pEvent_info */
		status = sme_qos_process_handoff_assoc_req_ev(mac, sessionId,
							     pEvent_info);
		break;
	case SME_QOS_CSR_HANDOFF_COMPLETE:
		/* nothing expected in pEvent_info */
		status =
			sme_qos_process_handoff_success_ev(mac, sessionId,
							   pEvent_info);
		break;
	case SME_QOS_CSR_PREAUTH_SUCCESS_IND:
		status =
			sme_qos_process_preauth_success_ind(mac, sessionId,
							    pEvent_info);
		break;
	case SME_QOS_CSR_SET_KEY_SUCCESS_IND:
		status =
			sme_qos_process_set_key_success_ind(mac, sessionId,
							    pEvent_info);
		break;
	case SME_QOS_CSR_DISCONNECT_ROAM_COMPLETE:
		sme_qos_process_disconnect_roam_ind(mac, sessionId);
		status = QDF_STATUS_SUCCESS;
		break;
	default:
		/* Err msg */
		sme_err("On Session %d Unknown Event %d received from CSR",
			sessionId, ind);
		break;
	}

	return status;
}

/*
 * sme_qos_get_acm_mask() - 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)
 *
 * mac - Pointer to the global MAC parameter structure.
 * pSirBssDesc - The event occurred of type sme_qos_csr_event_indType.
 * Return a bit mask indicating for which ACs AP has ACM set to 1
 */
uint8_t sme_qos_get_acm_mask(struct mac_context *mac, struct bss_description
				*pSirBssDesc, tDot11fBeaconIEs *pIes)
{
	enum qca_wlan_ac_type ac;
	uint8_t acm_mask = 0;

	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
		if (sme_qos_is_acm(mac, pSirBssDesc, ac, pIes))
			acm_mask = acm_mask | (1 << (QCA_WLAN_AC_VO - ac));
	}
	sme_debug("mask is 0x%x", acm_mask);
	return acm_mask;
}

/* Internal function definitions */

/**
 *  sme_qos_internal_setup_req() - The SME QoS internal setup request handling
 *                                 function.
 *
 *  @mac: Pointer to the global MAC parameter structure.
 *  @pQoSInfo: Pointer to struct sme_qos_wmmtspecinfo which contains the
 *              WMM TSPEC related info as defined above, provided by HDD
 *  @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
 *  @HDDcontext: A cookie passed by HDD to be used by SME during any QoS
 *               notification (through the callback) to HDD
 *  @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
 *  @QosFlowID: Identification per flow running on each AC generated by
 *              SME. It is only meaningful if the QoS setup for the flow is
 *              successful
 *  @buffered_cmd: tells us if the cmd was a buffered one or fresh from
 *                 client
 *
 *  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.
 *
 *  Return: QDF_STATUS_SUCCESS - Setup is successful.
 *          Other status means Setup request failed
 */
static enum sme_qos_statustype sme_qos_internal_setup_req(struct mac_context *mac,
					  uint8_t sessionId,
					  struct sme_qos_wmmtspecinfo *pQoSInfo,
					  sme_QosCallback QoSCallback,
					  void *HDDcontext,
					  enum sme_qos_wmmuptype UPType,
					  uint32_t QosFlowID,
					  bool buffered_cmd, bool hoRenewal)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	enum qca_wlan_ac_type ac;
	struct sme_qos_wmmtspecinfo Tspec_Info;
	enum sme_qos_states new_state = SME_QOS_CLOSED;
	struct sme_qos_flowinfoentry *pentry = NULL;
	struct sme_qos_cmdinfo cmd;
	enum sme_qos_statustype status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
	uint8_t tmask = 0;
	uint8_t new_tmask = 0;
	struct sme_qos_searchinfo search_key;
	QDF_STATUS hstatus;

	sme_debug("invoked on session %d for flow %d", sessionId, QosFlowID);
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	/* if caller sent an empty TSPEC, fill up with the default one */
	if (!pQoSInfo) {
		sme_warn("caller sent an empty QoS param list, using defaults");
		/* find the AC with UPType passed in */
		ac = sme_qos_up_to_ac(UPType);
		if (QCA_WLAN_AC_ALL == ac) {
			sme_err("invalid AC %d from UP %d", ac, UPType);

			return SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP;
		}
		Tspec_Info = sme_qos_cb.def_QoSInfo[ac];
	} else {
		/* find the AC */
		ac = sme_qos_up_to_ac(pQoSInfo->ts_info.up);
		if (QCA_WLAN_AC_ALL == ac) {
			sme_err("invalid AC %d from UP %d", ac,
				pQoSInfo->ts_info.up);

			return SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP;
		}
		/* validate QoS params */
		if (!sme_qos_validate_requested_params(mac, pQoSInfo,
							sessionId)) {
			sme_err("invalid params");
			return SME_QOS_STATUS_SETUP_INVALID_PARAMS_RSP;
		}
		Tspec_Info = *pQoSInfo;
	}
	pACInfo = &pSession->ac_info[ac];
	/* 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_qos_is_rsp_pending(sessionId, ac)) {
		sme_debug("buffering the setup request for flow %d in state %d since another request is pending",
			  QosFlowID, pACInfo->curr_state);
		/* we need to buffer the command */
		cmd.command = SME_QOS_SETUP_REQ;
		cmd.mac = mac;
		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_qos_buffer_cmd(&cmd, buffered_cmd);
		if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
			sme_err("couldn't buffer the setup request in state = %d",
				pACInfo->curr_state);
			return SME_QOS_STATUS_SETUP_FAILURE_RSP;
		}
		sme_debug("Buffered setup request for flow = %d", 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_qos_setup(mac, sessionId, &Tspec_Info, ac);
		sme_debug("On session %d with AC %d in state SME_QOS_LINK_UP sme_qos_setup returned with status %d",
			  sessionId, ac, status);

		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 = qdf_mem_malloc(sizeof(*pentry));
			if (!pentry)
				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) {
					sme_err("On session %d with AC %d in state SME_QOS_LINK_UP tspec_mask_status is %d but should not be set yet",
						sessionId, ac,
						pACInfo->tspec_mask_status);
					qdf_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(MAC_HANDLE(mac),
						    HDDcontext,
						    &pACInfo->
						    curr_QoSInfo
						    [SME_QOS_TSPEC_INDEX_0],
						    status, pentry->QosFlowID);
				}
				pentry->hoRenewal = 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;
			sme_debug("Creating entry on session %d at %pK with flowID %d",
				  sessionId, pentry, QosFlowID);
			csr_ll_insert_tail(&sme_qos_cb.flow_list, &pentry->link,
					   true);
		} else {
			/* unexpected status returned by sme_qos_setup() */
			sme_err("On session %d unexpected status %d returned by sme_qos_setup",
				sessionId, status);
			new_state = pACInfo->curr_state;
			if (buffered_cmd && hoRenewal)
				QoSCallback(MAC_HANDLE(mac), 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:
		sme_debug("Buffering setup request for flow %d in state = %d",
			  QosFlowID, pACInfo->curr_state);
		/* buffer cmd */
		cmd.command = SME_QOS_SETUP_REQ;
		cmd.mac = mac;
		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_qos_buffer_cmd(&cmd, buffered_cmd);
		if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
			sme_err("On session %d couldn't buffer the setup request for flow %d in state = %d",
				sessionId, QosFlowID, pACInfo->curr_state);
			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(mac) ||
			    sme_qos_is_acm(mac, pSession->assocInfo.bss_desc,
					   ac, NULL)) {
				sme_debug("tspec_mask_status = %d for AC = %d",
					  pACInfo->tspec_mask_status, ac);
				if (!pACInfo->tspec_mask_status) {
					sme_err("tspec_mask_status can't be 0 for ac: %d in state: %d",
						ac, pACInfo->curr_state);
					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 bidirecional, 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 down
					 * streams. 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
					sme_debug("wrong tmask = %d",
						  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;

			sme_debug("tmask = %d, new_tmask = %d in state = %d tspec_mask_status = %d for AC = %d",
				  tmask, new_tmask, pACInfo->curr_state,
				  pACInfo->tspec_mask_status, ac);
			if (tmask) {
				/* create the aggregate TSPEC */
				if (tmask != SME_QOS_TSPEC_MASK_BIT_1_2_SET) {
					hstatus =
						sme_qos_aggregate_params(
								&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;

					hstatus = sme_qos_aggregate_params(
							&Tspec_Info, &pACInfo->
							curr_QoSInfo
							[SME_QOS_TSPEC_INDEX_0],
							&pACInfo->
							requested_QoSInfo
							[tmask - 1]);
					if (hstatus == QDF_STATUS_SUCCESS) {
						hstatus =
							sme_qos_aggregate_params
								(&pACInfo->
								curr_QoSInfo
							[SME_QOS_TSPEC_INDEX_1],
								&pACInfo->
						requested_QoSInfo[tmask - 1],
								NULL);
					}
				}

				if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
					/* err msg */
					sme_err("failed to aggregate params");
					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 {
			sme_err("no flows running for ac = %d while in state = %d",
				ac, pACInfo->curr_state);
			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_qos_setup(mac, sessionId,
				      &pACInfo->requested_QoSInfo[tmask - 1],
					ac);
		sme_debug("On session %d with AC %d in state SME_QOS_QOS_ON sme_qos_setup returned with status %d",
			  sessionId, ac, status);
		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 = qdf_mem_malloc(sizeof(*pentry));
			if (!pentry)
				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;
			sme_debug("Creating flow %d", 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(MAC_HANDLE(mac),
						    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) {
						qdf_mem_zero(&search_key,
							     sizeof
						(struct sme_qos_searchinfo));
						search_key.key.ac_type = ac;
						search_key.index =
						SME_QOS_SEARCH_KEY_INDEX_2;
						search_key.sessionId =
							sessionId;
						hstatus =
						sme_qos_find_all_in_flow_list
							(mac, search_key,
							sme_qos_setup_fnp);
						if (!QDF_IS_STATUS_SUCCESS
							    (hstatus)) {
							sme_err("couldn't notify other entries on this AC =%d",
								ac);
						}
					}
				}
				pentry->hoRenewal = 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;
			sme_debug("On session %d creating entry at %pK with flowID %d",
				  sessionId, pentry, QosFlowID);
			csr_ll_insert_tail(&sme_qos_cb.flow_list, &pentry->link,
					   true);
		} else {
			/* unexpected status returned by sme_qos_setup() */
			sme_err("On session %d unexpected status %d returned by sme_qos_setup",
				sessionId, status);
			new_state = pACInfo->curr_state;
		}
		break;
	case SME_QOS_CLOSED:
	case SME_QOS_INIT:
	default:
		sme_err("setup requested in unexpected state = %d",
			pACInfo->curr_state);
		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_qos_state_transition(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_qos_process_buffered_cmd(sessionId);

	return status;
}

/**
 * sme_qos_internal_modify_req() - The SME QoS internal function to request
 *  for modification of certain QoS params on a flow running on a particular AC.
 * @mac: Pointer to the global MAC parameter structure.
 * @pQoSInfo: Pointer to struct sme_qos_wmmtspecinfo which contains the
 *            WMM TSPEC related info as defined above, provided by HDD
 * @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
 *
 * 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.
 *
 * Return: SME_QOS_STATUS_SETUP_SUCCESS_RSP - Modification is successful.
 *         Other status means request failed
 */
static enum sme_qos_statustype sme_qos_internal_modify_req(struct mac_context *mac,
					  struct sme_qos_wmmtspecinfo *pQoSInfo,
					  uint32_t QosFlowID,
					  bool buffered_cmd)
{
	tListElem *pEntry = NULL;
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	struct sme_qos_flowinfoentry *pNewEntry = NULL;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	enum qca_wlan_ac_type ac;
	enum sme_qos_states new_state = SME_QOS_CLOSED;
	enum sme_qos_statustype status =
		SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP;
	struct sme_qos_wmmtspecinfo Aggr_Tspec_Info;
	struct sme_qos_searchinfo search_key;
	struct sme_qos_cmdinfo cmd;
	uint8_t sessionId;
	QDF_STATUS hstatus;

	sme_debug("invoked for flow %d", QosFlowID);

	qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo));
	/* 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_qos_find_in_flow_list(search_key);
	if (!pEntry) {
		/* Err msg */
		sme_err("no match found for flowID = %d", QosFlowID);
		return SME_QOS_STATUS_MODIFY_SETUP_INVALID_PARAMS_RSP;
	}
	/* find the AC */
	flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link);
	ac = flow_info->ac_type;

	sessionId = flow_info->sessionId;
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	pACInfo = &pSession->ac_info[ac];

	/* validate QoS params */
	if (!sme_qos_validate_requested_params(mac, pQoSInfo, sessionId)) {
		sme_err("invalid params");
		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)) {
		sme_err("Modification of direction/tid/up is not allowed");

		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)) {
		sme_err("the addts parameters are same as last request, dropping the current request");

		return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP;
	}

	/* 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_qos_is_rsp_pending(sessionId, ac)) {
		sme_debug("buffering the modify request for flow %d in state %d since another request is pending",
			  QosFlowID, pACInfo->curr_state);
		/* we need to buffer the command */
		cmd.command = SME_QOS_MODIFY_REQ;
		cmd.mac = mac;
		cmd.sessionId = sessionId;
		cmd.u.modifyCmdInfo.QosFlowID = QosFlowID;
		cmd.u.modifyCmdInfo.QoSInfo = *pQoSInfo;
		hstatus = sme_qos_buffer_cmd(&cmd, buffered_cmd);
		if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
			sme_err("couldn't buffer the modify request in state = %d",
				pACInfo->curr_state);
			return SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP;
		}
		sme_debug("Buffered modify request for flow = %d", 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 = qdf_mem_malloc(sizeof(*pNewEntry));
		if (!pNewEntry)
			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;
		sme_debug("On session %d creating modified entry at %pK with flowID %d",
			  sessionId, pNewEntry, pNewEntry->QosFlowID);
		/* add the new entry under construction to the Flow List */
		csr_ll_insert_tail(&sme_qos_cb.flow_list, &pNewEntry->link,
				   true);
		/* update TSPEC with the new param set */
		hstatus = sme_qos_update_params(sessionId,
						ac, pNewEntry->tspec_mask,
						&Aggr_Tspec_Info);
		if (QDF_IS_STATUS_SUCCESS(hstatus)) {
			pACInfo->requested_QoSInfo[pNewEntry->tspec_mask - 1] =
				Aggr_Tspec_Info;
			/* if ACM, send out a new ADDTS */
			status = sme_qos_setup(mac, sessionId,
					       &pACInfo->
					       requested_QoSInfo[pNewEntry->
								tspec_mask - 1],
					       ac);
			sme_debug("On session %d with AC %d in state SME_QOS_QOS_ON sme_qos_setup returned with status %d",
				  sessionId, ac, status);

			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;

				qdf_mem_zero(&search_key,
					     sizeof(struct sme_qos_searchinfo));
				/* 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_qos_find_all_in_flow_list(mac,
							search_key,
							sme_qos_modify_fnp);
				if (!QDF_IS_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;
						qdf_mem_zero(&search_key,
							     sizeof
						(struct sme_qos_searchinfo));
						search_key.key.ac_type = ac;
						search_key.index =
						SME_QOS_SEARCH_KEY_INDEX_2;
						search_key.sessionId =
							sessionId;
						hstatus =
						sme_qos_find_all_in_flow_list
							(mac, search_key,
								sme_qos_modification_notify_fnp);
						if (!QDF_IS_STATUS_SUCCESS
							    (hstatus)) {
							sme_err("couldn't notify other entries on this AC =%d",
								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(MAC_HANDLE(mac),
							       flow_info->
							       HDDcontext,
							       &pACInfo->
							       curr_QoSInfo
							       [pNewEntry->
								tspec_mask - 1],
							       status,
							       flow_info->
							       QosFlowID);
				}

			} else {
				/* unexpected status returned by
				 * sme_qos_setup()
				 */
				sme_err("On session %d unexpected status %d returned by sme_qos_setup",
					sessionId, status);
				new_state = SME_QOS_QOS_ON;
			}
		} else {
			/* err msg */
			sme_err("sme_qos_update_params() failed");
			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_qos_state_transition(sessionId, ac, new_state);
		else
			pACInfo->prev_state = SME_QOS_REQUESTED;
		break;
	case SME_QOS_HANDOFF:
	case SME_QOS_REQUESTED:
		sme_debug("Buffering modify request for flow %d in state = %d",
			  QosFlowID, pACInfo->curr_state);
		/* buffer cmd */
		cmd.command = SME_QOS_MODIFY_REQ;
		cmd.mac = mac;
		cmd.sessionId = sessionId;
		cmd.u.modifyCmdInfo.QosFlowID = QosFlowID;
		cmd.u.modifyCmdInfo.QoSInfo = *pQoSInfo;
		hstatus = sme_qos_buffer_cmd(&cmd, buffered_cmd);
		if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
			sme_err("couldn't buffer the modify request in state = %d",
				pACInfo->curr_state);
			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:
		sme_err("modify requested in unexpected state = %d",
			pACInfo->curr_state);
		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_qos_process_buffered_cmd(sessionId);

	return status;
}

/**
 * sme_qos_internal_release_req() - release QOS flow on a particular AC
 * @mac: Pointer to the global MAC parameter structure.
 * @sessionId: sessionId returned by sme_open_session.
 * @QosFlowID: Identification per flow running on each AC generated by SME
 *             It is only meaningful if the QoS setup for the flow is successful
 *
 * The SME QoS internal function to request
 * for releasing a QoS flow running on a particular AC.

 * Return: QDF_STATUS_SUCCESS - Release is successful.
 */
static enum sme_qos_statustype sme_qos_internal_release_req(struct mac_context *mac,
					       uint8_t sessionId,
					       uint32_t QosFlowID,
					       bool buffered_cmd)
{
	tListElem *pEntry = NULL;
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	struct sme_qos_flowinfoentry *pDeletedFlow = NULL;
	enum qca_wlan_ac_type ac;
	enum sme_qos_states new_state = SME_QOS_CLOSED;
	enum sme_qos_statustype status = SME_QOS_STATUS_RELEASE_FAILURE_RSP;
	struct sme_qos_wmmtspecinfo Aggr_Tspec_Info;
	struct sme_qos_searchinfo search_key;
	struct sme_qos_cmdinfo cmd;
	tCsrRoamModifyProfileFields modifyProfileFields;
	bool deltsIssued = false;
	QDF_STATUS hstatus;
	bool biDirectionalFlowsPresent = false;
	bool uplinkFlowsPresent = false;
	bool downlinkFlowsPresent = false;
	tListElem *pResult = NULL;
	mac_handle_t mac_hdl = MAC_HANDLE(mac);

	sme_debug("invoked for flow %d", QosFlowID);

	qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo));
	/* 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_qos_find_in_flow_list(search_key);

	if (!pEntry) {
		/* Err msg */
		sme_err("no match found for flowID = %d", QosFlowID);

		pSession = &sme_qos_cb.sessionInfo[sessionId];
		if (!buffered_cmd &&
		    !csr_ll_is_list_empty(&pSession->bufferedCommandList,
					  false)) {
			cmd.command = SME_QOS_RELEASE_REQ;
			cmd.mac = mac;
			cmd.sessionId = sessionId;
			cmd.u.releaseCmdInfo.QosFlowID = QosFlowID;
			hstatus = sme_qos_buffer_cmd(&cmd, buffered_cmd);
			if (QDF_IS_STATUS_SUCCESS(hstatus)) {
				sme_debug("Buffered release request for flow = %d",
					  QosFlowID);
				return SME_QOS_STATUS_RELEASE_REQ_PENDING_RSP;
			}
		}
		return SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP;
	}
	/* find the AC */
	flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link);
	ac = flow_info->ac_type;
	sessionId = flow_info->sessionId;

	if (!CSR_IS_SESSION_VALID(mac, sessionId)) {
		sme_err("Session Id: %d is invalid", sessionId);
		return status;
	}

	pSession = &sme_qos_cb.sessionInfo[sessionId];
	pACInfo = &pSession->ac_info[ac];
	/* 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_qos_is_rsp_pending(sessionId, ac)) {
		sme_debug("buffering the release request for flow %d in state %d since another request is pending",
			  QosFlowID, pACInfo->curr_state);
		/* we need to buffer the command */
		cmd.command = SME_QOS_RELEASE_REQ;
		cmd.mac = mac;
		cmd.sessionId = sessionId;
		cmd.u.releaseCmdInfo.QosFlowID = QosFlowID;
		hstatus = sme_qos_buffer_cmd(&cmd, buffered_cmd);
		if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
			sme_err("couldn't buffer the release request in state = %d",
				pACInfo->curr_state);
			return SME_QOS_STATUS_RELEASE_FAILURE_RSP;
		}
		sme_debug("Buffered release request for flow = %d", 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:
		sme_debug("tspec_mask_status = %d for AC = %d with entry tspec_mask = %d",
			  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-diretional
			 * 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
			 * aggregaed 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) {
				qdf_mem_zero(&search_key,
					     sizeof(struct sme_qos_searchinfo));
				/* 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_qos_find_in_flow_list(search_key);
				if (pResult)
					biDirectionalFlowsPresent = 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_qos_find_in_flow_list(search_key);
					if (pResult)
						uplinkFlowsPresent = true;

					/* Check if downlink flows exist */
					search_key.direction =
						SME_QOS_WMM_TS_DIR_DOWNLINK;
					pResult =
					sme_qos_find_in_flow_list(search_key);
					if (pResult)
						downlinkFlowsPresent = true;

					if (uplinkFlowsPresent
					    && downlinkFlowsPresent) {
						/* Need to split the uni-
						 * directional flows into
						 * SME_QOS_TSPEC_INDEX_0 and
						 * SME_QOS_TSPEC_INDEX_1
						 */

						qdf_mem_zero(&search_key,
							     sizeof
						(struct sme_qos_searchinfo));
						/* 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_qos_update_tspec_mask
							(sessionId, search_key,
						SME_QOS_TSPEC_MASK_BIT_2_SET);

						/* Aggregate all downstream
						 * flows
						 */
						hstatus =
							sme_qos_update_params
								(sessionId, ac,
						SME_QOS_TSPEC_MASK_BIT_2_SET,
							&Aggr_Tspec_Info);

						sme_err("On session %d buffering the AddTS request for AC %d in state %d as Addts is pending on other Tspec index of this AC",
							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 susequent logic.
						 */
						cmd.command =
							SME_QOS_RESEND_REQ;
						cmd.mac = mac;
						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 (!QDF_IS_STATUS_SUCCESS
							    (sme_qos_buffer_cmd
								    (&cmd,
								false))) {
							sme_err("On session %d unable to buffer the AddTS request for AC %d TSPEC %d in state %d",
								sessionId, ac,
								SME_QOS_TSPEC_MASK_BIT_2_SET,
								pACInfo->
								curr_state);

							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_qos_update_params(sessionId,
						ac, flow_info->tspec_mask,
							&Aggr_Tspec_Info);
			if (QDF_IS_STATUS_SUCCESS(hstatus)) {
				pACInfo->requested_QoSInfo[flow_info->
							   tspec_mask - 1] =
					Aggr_Tspec_Info;
				/* if ACM, send out a new ADDTS */
				status = sme_qos_setup(mac, sessionId,
						       &pACInfo->
						       requested_QoSInfo
						       [flow_info->tspec_mask -
							1], ac);
				sme_debug("On session %d with AC %d in state SME_QOS_QOS_ON sme_qos_setup returned with status %d",
					  sessionId, ac, status);

				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 */
					sme_debug("Deleting entry at %pK with flowID %d",
						  flow_info, QosFlowID);
					csr_ll_remove_entry(&sme_qos_cb.
						flow_list, pEntry, true);
					pDeletedFlow = flow_info;
					if (SME_QOS_STATUS_SETUP_SUCCESS_APSD_SET_ALREADY == status) {
						qdf_mem_zero(&search_key,
							     sizeof
						(struct sme_qos_searchinfo));
						search_key.key.ac_type = ac;
						search_key.index =
						SME_QOS_SEARCH_KEY_INDEX_2;
						search_key.sessionId =
							sessionId;
						hstatus =
						sme_qos_find_all_in_flow_list
							(mac, search_key,
							sme_qos_setup_fnp);
						if (!QDF_IS_STATUS_SUCCESS
							    (hstatus)) {
							sme_err("couldn't notify other entries on this AC =%d",
								ac);
						}
					}
					status =
					SME_QOS_STATUS_RELEASE_SUCCESS_RSP;
					if (buffered_cmd) {
						flow_info->QoSCallback(MAC_HANDLE(mac),
								     flow_info->
								     HDDcontext,
								     &pACInfo->
								    curr_QoSInfo
								    [flow_info->
								tspec_mask - 1],
								       status,
								     flow_info->
								     QosFlowID);
					}
				} else {
					/* unexpected status returned by
					 * sme_qos_setup()
					 */
					sme_err("On session %d unexpected status %d returned by sme_qos_setup",
						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 */
					sme_debug("On session %d deleting entry at %pK with flowID %d",
						  sessionId, flow_info,
						  QosFlowID);
					csr_ll_remove_entry(&sme_qos_cb.
								flow_list,
							    pEntry, true);
					pDeletedFlow = flow_info;
					if (buffered_cmd) {
						flow_info->QoSCallback(MAC_HANDLE(mac),
								     flow_info->
								     HDDcontext,
								      &pACInfo->
								    curr_QoSInfo
								    [flow_info->
								tspec_mask - 1],
								       status,
								     flow_info->
								     QosFlowID);
					}
				}
			} else {
				/* err msg */
				sme_err("sme_qos_update_params() failed");
				new_state = SME_QOS_LINK_UP;
				if (buffered_cmd) {
					flow_info->QoSCallback(MAC_HANDLE(mac),
							       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;
			/* check if delts needs to be sent */
			if (CSR_IS_ADDTS_WHEN_ACMOFF_SUPPORTED(mac) ||
			    sme_qos_is_acm(mac, pSession->assocInfo.bss_desc,
					   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 << (QCA_WLAN_AC_VO - ac));
					/* Also update modifyProfileFields.
					 * uapsd_mask in CSR for consistency
					 */
					csr_get_modify_profile_fields(mac,
								     flow_info->
								      sessionId,
							&modifyProfileFields);
					modifyProfileFields.uapsd_mask =
						pSession->apsdMask;
					csr_set_modify_profile_fields(mac,
								     flow_info->
								      sessionId,
							&modifyProfileFields);
					if (!pSession->apsdMask) {
						/* this session no longer needs
						 * UAPSD do any sessions still
						 * require UAPSD?
						 */
						if (!sme_qos_is_uapsd_active())
							/* No sessions require
							 * UAPSD so turn it off
							 * (really don't care
							 * when PMC stops it)
							 */
							sme_ps_uapsd_disable(
							      mac_hdl,
							      sessionId);
					}
				}
				if (SME_QOS_RELEASE_DEFAULT ==
							pACInfo->relTrig) {
					/* send delts */
					hstatus =
						qos_issue_command(mac,
								sessionId,
							eSmeCommandDelTs,
								  NULL, ac,
								  flow_info->
								  tspec_mask);
					if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
						/* err msg */
						sme_err("sme_qos_del_ts_req() failed");
						status =
							SME_QOS_STATUS_RELEASE_FAILURE_RSP;
					} else {
						pACInfo->tspec_mask_status &=
						SME_QOS_TSPEC_MASK_BIT_1_2_SET
						& (~flow_info->tspec_mask);
						deltsIssued = true;
					}
				} else {
					pACInfo->tspec_mask_status &=
						SME_QOS_TSPEC_MASK_BIT_1_2_SET &
						(~flow_info->tspec_mask);
					deltsIssued = true;
				}
			} else if (pSession->apsdMask &
				(1 << (QCA_WLAN_AC_VO - ac))) {
				/* reassoc logic */
				csr_get_modify_profile_fields(mac, sessionId,
							  &modifyProfileFields);
				modifyProfileFields.uapsd_mask |=
					pSession->apsdMask;
				modifyProfileFields.uapsd_mask &=
					~(1 << (QCA_WLAN_AC_VO - ac));
				pSession->apsdMask &=
					~(1 << (QCA_WLAN_AC_VO - ac));
				if (!pSession->apsdMask) {
					/* this session no longer needs UAPSD
					 * do any sessions still require UAPSD?
					 */
					if (!sme_qos_is_uapsd_active())
						/* No sessions require UAPSD so
						 * turn it off (really don't
						 * care when PMC stops it)
						 */
						sme_ps_uapsd_disable(
							mac_hdl, sessionId);
				}
				hstatus = sme_qos_request_reassoc(mac,
								sessionId,
							&modifyProfileFields,
								  false);
				if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
					/* err msg */
					sme_err("Reassoc failed");
					status =
					SME_QOS_STATUS_RELEASE_FAILURE_RSP;
				} else {
					/* no need to wait */
					pACInfo->reassoc_pending = false;
					pACInfo->prev_state = SME_QOS_LINK_UP;
					pACInfo->tspec_pending = 0;
				}
			} else {
				sme_debug("nothing to do for AC = %d", ac);
			}

			if (SME_QOS_RELEASE_BY_AP == pACInfo->relTrig) {
				flow_info->QoSCallback(MAC_HANDLE(mac),
						       flow_info->HDDcontext,
						       &pACInfo->
						       curr_QoSInfo[flow_info->
								    tspec_mask -
								    1],
					SME_QOS_STATUS_RELEASE_QOS_LOST_IND,
						       flow_info->QosFlowID);

				sme_debug("Deleting entry at %pK with flowID %d",
					  flow_info, flow_info->QosFlowID);
			} else if (buffered_cmd) {
				flow_info->QoSCallback(MAC_HANDLE(mac),
						       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 {
				sme_debug("Exceeded the array bounds of pACInfo->num_flows");
				return
				SME_QOS_STATUS_RELEASE_INVALID_PARAMS_RSP;
			}

			if (false == deltsIssued) {
				qdf_mem_zero(&pACInfo->
					curr_QoSInfo[flow_info->
					tspec_mask - 1],
					sizeof(struct sme_qos_wmmtspecinfo));
			}
			qdf_mem_zero(&pACInfo->
				     requested_QoSInfo[flow_info->tspec_mask -
						       1],
				     sizeof(struct sme_qos_wmmtspecinfo));
			pACInfo->num_flows[flow_info->tspec_mask - 1]--;
			/* delete the entry from Flow List */
			sme_debug("On session %d deleting entry at %pK with flowID %d",
				  sessionId, flow_info, QosFlowID);
			csr_ll_remove_entry(&sme_qos_cb.flow_list, pEntry,
					    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_qos_state_transition(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.mac = mac;
		cmd.sessionId = sessionId;
		cmd.u.releaseCmdInfo.QosFlowID = QosFlowID;
		hstatus = sme_qos_buffer_cmd(&cmd, buffered_cmd);
		if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
			sme_err("couldn't buffer the release request in state = %d",
				pACInfo->curr_state);
			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 */
		sme_err("release request in unexpected state = %d",
			pACInfo->curr_state);
		break;
	}
	/* if we deleted a flow, reclaim the memory */
	if (pDeletedFlow)
		qdf_mem_free(pDeletedFlow);

	if (SME_QOS_STATUS_RELEASE_SUCCESS_RSP == status)
		(void)sme_qos_process_buffered_cmd(sessionId);

	return status;
}

/**
 * sme_qos_setup() - internal SME QOS setup function.
 * @mac: Pointer to the global MAC parameter structure.
 * @sessionId: Session upon which setup is being performed
 * @pTspec_Info: Pointer to struct sme_qos_wmmtspecinfo which contains the WMM
 *               TSPEC related info as defined above
 * @ac: Enumeration of the various EDCA Access Categories.
 *
 * 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.
 * 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
 *
 * Return: SME_QOS_STATUS_SETUP_SUCCESS_RSP if the setup is successful'
 */
static enum sme_qos_statustype sme_qos_setup(struct mac_context *mac,
				uint8_t sessionId,
				struct sme_qos_wmmtspecinfo *pTspec_Info,
				enum qca_wlan_ac_type ac)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	enum sme_qos_statustype status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
	tDot11fBeaconIEs *pIes = NULL;
	tCsrRoamModifyProfileFields modifyProfileFields;
	QDF_STATUS hstatus;

	if (!CSR_IS_SESSION_VALID(mac, sessionId)) {
		sme_err("Session Id %d is invalid", sessionId);
		return status;
	}
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	if (!pSession->sessionActive) {
		sme_err("Session %d is inactive", sessionId);
		return status;
	}
	if (!pSession->assocInfo.bss_desc) {
		sme_err("Session %d has an Invalid BSS Descriptor", sessionId);
		return status;
	}
	hstatus = csr_get_parsed_bss_description_ies(mac,
						   pSession->assocInfo.bss_desc,
						      &pIes);
	if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
		sme_err("On session %d unable to parse BSS IEs", sessionId);
		return status;
	}

	/* success so pIes was allocated */

	if (!CSR_IS_QOS_BSS(pIes)) {
		sme_err("On session %d AP doesn't support QoS", sessionId);
		qdf_mem_free(pIes);
		/* notify HDD through the synchronous status msg */
		return SME_QOS_STATUS_SETUP_NOT_QOS_AP_RSP;
	}

	sme_debug("UAPSD/PSB set %d: ", pTspec_Info->ts_info.psb);

	pACInfo = &pSession->ac_info[ac];
	do {
		/* is ACM enabled for this AC? */
		if (CSR_IS_ADDTS_WHEN_ACMOFF_SUPPORTED(mac) ||
		    sme_qos_is_acm(mac, pSession->assocInfo.bss_desc,
				   ac, NULL)) {
			/* ACM is enabled for this AC so we must send an
			 * AddTS
			 */
			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
				 */
				sme_err("On session %d AP doesn't support APSD",
					sessionId);
				break;
			}

			if (SME_QOS_MAX_TID == pTspec_Info->ts_info.tid) {
				/* App didn't set TID, generate one */
				pTspec_Info->ts_info.tid =
					(uint8_t) (SME_QOS_WMM_UP_NC -
						   pTspec_Info->ts_info.up);
			}
			/* addts logic */
			hstatus =
				qos_issue_command(mac, sessionId,
						eSmeCommandAddTs,
						  pTspec_Info, ac, 0);
			if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
				sme_err("sme_qos_add_ts_req() failed");
				break;
			}
			sme_debug("On session %d AddTS on AC %d is pending",
				  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 << (QCA_WLAN_AC_VO - ac))) {
				/* APSD was formerly enabled on this AC but is
				 * no longer required so we must reassociate
				 */
				sme_debug("On session %d reassoc needed to disable APSD on AC %d",
					  sessionId, ac);
				csr_get_modify_profile_fields(mac, sessionId,
							  &modifyProfileFields);
				modifyProfileFields.uapsd_mask |=
					pSession->apsdMask;
				modifyProfileFields.uapsd_mask &=
					~(1 << (QCA_WLAN_AC_VO - ac));
				hstatus =
					sme_qos_request_reassoc(mac, sessionId,
							&modifyProfileFields,
								false);
				if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
					/* err msg */
					sme_err("Unable to request reassociation");
					break;
				} else {
					sme_debug("On session %d reassociation to enable APSD on AC %d is pending",
						  sessionId, ac);
					status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP;
					pACInfo->reassoc_pending = true;
				}
			} else {
				/* we don't need APSD on this AC and we don't
				 * currently have APSD on this AC
				 */
				sme_debug("Request is not looking for APSD & Admission Control isn't mandatory for the AC");
				/* 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
			 */
			sme_err("On session %d AP doesn't support APSD",
				sessionId);
			break;
		} else if (pSession->
			   apsdMask & (1 << (QCA_WLAN_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;
			sme_debug("Request is looking for APSD and it is already set for the AC");
			break;
		} else {
			/* application is looking for APSD but it is not enabled on this
			 * AC so we need to reassociate
			 */
			sme_debug("On session %d reassoc needed to enable APSD on AC %d",
				  sessionId, ac);
			/* reassoc logic */
			/* update the UAPSD mask to include the new */
			/* AC on which APSD is requested */
			csr_get_modify_profile_fields(mac, sessionId,
						&modifyProfileFields);
			modifyProfileFields.uapsd_mask |=
					pSession->apsdMask;
			modifyProfileFields.uapsd_mask |=
					1 << (QCA_WLAN_AC_VO - ac);
			hstatus = sme_qos_request_reassoc(mac, sessionId,
							&modifyProfileFields,
							false);
			if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
				/* err msg */
				sme_err("Unable to request reassociation");
				break;
			} else {
				sme_debug("On session %d reassociation to enable APSD on AC %d is pending",
					  sessionId, ac);
				status = SME_QOS_STATUS_SETUP_REQ_PENDING_RSP;
				pACInfo->reassoc_pending = true;
			}
		}
	} while (0);

	qdf_mem_free(pIes);
	return status;
}

/* 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!
 */
static QDF_STATUS sme_qos_process_set_key_success_ind(struct mac_context *mac,
					   uint8_t sessionId, void *pEvent_info)
{
	sme_debug("Set Key complete");
	(void)sme_qos_process_buffered_cmd(sessionId);

	return QDF_STATUS_SUCCESS;
}

#ifdef FEATURE_WLAN_ESE
/**
 * sme_qos_ese_save_tspec_response() - save TSPEC parameters.
 * @mac: Pointer to the global MAC parameter structure.
 * @sessionId: SME session ID
 * @pTspec: Pointer to the TSPEC IE from the reassoc rsp
 * @ac:  Access Category for which this TSPEC rsp is received
 * @tspecIndex: flow/direction
 *
 * This function saves the TSPEC parameters that came along in the TSPEC IE
 * in the reassoc response
 *
 * Return: QDF_STATUS_SUCCESS - Release is successful.
 */
static QDF_STATUS
sme_qos_ese_save_tspec_response(struct mac_context *mac, uint8_t sessionId,
				tDot11fIEWMMTSPEC *pTspec, uint8_t ac,
				uint8_t tspecIndex)
{
	tpSirAddtsRsp pAddtsRsp =
		&sme_qos_cb.sessionInfo[sessionId].ac_info[ac].
		addTsRsp[tspecIndex];

	ac = sme_qos_up_to_ac_map[pTspec->user_priority];

	qdf_mem_zero(pAddtsRsp, sizeof(tSirAddtsRsp));

	pAddtsRsp->messageType = eWNI_SME_ADDTS_RSP;
	pAddtsRsp->length = sizeof(tSirAddtsRsp);
	pAddtsRsp->rc = QDF_STATUS_SUCCESS;
	pAddtsRsp->sessionId = sessionId;
	pAddtsRsp->rsp.dialogToken = 0;
	pAddtsRsp->rsp.status = STATUS_SUCCESS;
	pAddtsRsp->rsp.wmeTspecPresent = pTspec->present;
	sme_debug("Copy Tspec to local data structure ac=%d, tspecIdx=%d",
		  ac, tspecIndex);

	if (pAddtsRsp->rsp.wmeTspecPresent)
		/* Copy TSPEC params received in assoc response to addts
		 * response
		 */
		convert_wmmtspec(mac, &pAddtsRsp->rsp.tspec, pTspec);

	return QDF_STATUS_SUCCESS;
}

/**
 * sme_qos_ese_process_reassoc_tspec_rsp() - process ese reassoc tspec response
 * @mac: Pointer to the global MAC parameter structure.
 * @sessionId: SME session ID
 * @pEven_info: Pointer to the smeJoinRsp structure
 *
 * 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 reasso req from the STA would carry the TSPEC parameters which were
 * already negotiated with the older AP.
 *
 * Return: QDF_STATUS_SUCCESS - Release is successful.
 */
static
QDF_STATUS sme_qos_ese_process_reassoc_tspec_rsp(struct mac_context *mac,
						 uint8_t sessionId,
						 void *pEvent_info)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	tDot11fIEWMMTSPEC *pTspecIE = NULL;
	struct csr_roam_session *pCsrSession = NULL;
	struct csr_roam_connectedinfo *pCsrConnectedInfo = NULL;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;
	uint8_t ac, numTspec, cnt;
	uint8_t tspec_flow_index, tspec_mask_status;
	uint32_t tspecIeLen;

	pCsrSession = CSR_GET_SESSION(mac, sessionId);
	if (!pCsrSession) {
		sme_err("session %d not found", sessionId);
		return QDF_STATUS_E_FAILURE;
	}
	pCsrConnectedInfo = &pCsrSession->connectedInfo;
	pSession = &sme_qos_cb.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 atleast equal to the one TSPEC IE */
	tspecIeLen = pCsrConnectedInfo->nTspecIeLength;
	if (tspecIeLen < sizeof(tDot11fIEWMMTSPEC)) {
		sme_err("ESE Tspec IE len %d less than min %zu",
			tspecIeLen, sizeof(tDot11fIEWMMTSPEC));
		return QDF_STATUS_E_FAILURE;
	}

	sme_warn("TspecLen = %d, pbFrames = %pK, pTspecIE = %pK",
		 tspecIeLen, pCsrConnectedInfo->pbFrames, pTspecIE);

	numTspec = (tspecIeLen) / sizeof(tDot11fIEWMMTSPEC);
	for (cnt = 0; cnt < numTspec; cnt++) {
		ac = sme_qos_up_to_ac(pTspecIE->user_priority);
		if (ac >= QCA_WLAN_AC_ALL) {
			sme_err("ac %d more than it`s max value", ac);
			return QDF_STATUS_E_FAILURE;
		}
		pACInfo = &pSession->ac_info[ac];
		tspec_mask_status = pACInfo->tspec_mask_status;
		sme_warn("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)) {
				sme_warn("Found Tspec entry flow = %d AC = %d",
					 tspec_flow_index, ac);
				sme_qos_ese_save_tspec_response(mac, sessionId,
								pTspecIE, ac,
							tspec_flow_index);
			} else {
				sme_warn("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_qos_ft_aggr_qos_req(mac, sessionId);

	return status;
}

/**
 * sme_qos_copy_tspec_info() - copy tspec info.
 * @mac: Pointer to the global MAC parameter structure.
 * @pTspec_Info: source structure
 * @pTspec: destination structure
 *
 * This function copies the existing TSPEC parameters from the source structure
 * to the destination structure.
 *
 * Return: None
 */
static void sme_qos_copy_tspec_info(struct mac_context *mac,
				    struct sme_qos_wmmtspecinfo *pTspec_Info,
				    struct mac_tspec_ie *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;

	/* Make sure UAPSD is allowed */
	if (pTspec_Info->ts_info.psb)
		pTspec->tsinfo.traffic.psb = pTspec_Info->ts_info.psb;
	else {
		pTspec->tsinfo.traffic.psb = 0;
		pTspec_Info->ts_info.psb = 0;
	}
	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;

	sme_debug("up = %d, tid = %d", pTspec_Info->ts_info.up,
		  pTspec_Info->ts_info.tid);
}

/**
 * sme_qos_ese_retrieve_tspec_info() - retrieve tspec info.
 * @mac: Pointer to the global MAC parameter structure.
 * @sessionId: SME session ID
 * @pTspecInfo: Pointer to the structure to carry back the TSPEC parameters
 *
 * 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.
 *
 * Return: uint8_t - number of existing negotiated TSPECs
 */
uint8_t sme_qos_ese_retrieve_tspec_info(struct mac_context *mac_ctx,
	 uint8_t session_id, tTspecInfo *tspec_info)
{
	struct sme_qos_sessioninfo *session;
	struct sme_qos_acinfo *ac_info;
	uint8_t ac, num_tspec = 0;
	tTspecInfo *dst_tspec = tspec_info;
	uint8_t tspec_mask;
	uint8_t tspec_pending;

	/* TODO: Check if TSPEC has already been established
	 * if not return
	 */
	session = &sme_qos_cb.sessionInfo[session_id];
	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
		volatile uint8_t index = 0;

		ac_info = &session->ac_info[ac];
		tspec_pending = ac_info->tspec_pending;
		tspec_mask = ac_info->tspec_mask_status;
		do {
			/*
			 * If a tspec status is pending, take
			 * requested_QoSInfo for RIC request,
			 * else use curr_QoSInfo for the
			 * RIC request
			 */
			if ((tspec_mask & SME_QOS_TSPEC_MASK_BIT_1_SET)
				&& (tspec_pending &
				SME_QOS_TSPEC_MASK_BIT_1_SET)){
				sme_qos_copy_tspec_info(mac_ctx,
					&ac_info->requested_QoSInfo[index],
					&dst_tspec->tspec);
				dst_tspec->valid = true;
				num_tspec++;
				dst_tspec++;
			} else if ((tspec_mask & SME_QOS_TSPEC_MASK_BIT_1_SET)
				&& !(tspec_pending &
				SME_QOS_TSPEC_MASK_BIT_1_SET)){
				sme_qos_copy_tspec_info(mac_ctx,
					&ac_info->curr_QoSInfo[index],
					&dst_tspec->tspec);
				dst_tspec->valid = true;
				num_tspec++;
				dst_tspec++;
			}
			tspec_mask >>= 1;
			tspec_pending >>= 1;
			index++;
		} while (tspec_mask);
	}
	return num_tspec;
}

#endif

#ifdef WLAN_FEATURE_HOST_ROAM
static
QDF_STATUS sme_qos_create_tspec_ricie(struct mac_context *mac,
				      struct sme_qos_wmmtspecinfo *tspec_info,
				      uint8_t *ric_buffer, uint32_t *ric_length,
				      uint8_t *ric_identifier)
{
	tDot11fIERICDataDesc *ric_ie;
	uint32_t status;

	if (!ric_buffer || !ric_identifier || ric_length ==
								NULL) {
		sme_err("RIC data is NULL, %pK, %pK, %pK",
			ric_buffer, ric_identifier, ric_length);
		return QDF_STATUS_E_FAILURE;
	}

	ric_ie = qdf_mem_malloc(sizeof(*ric_ie));
	if (!ric_ie) {
		sme_err("malloc failed for ric IE");
		return QDF_STATUS_E_NOMEM;
	}

	ric_ie->present = 1;
	ric_ie->RICData.present = 1;
	ric_ie->RICData.resourceDescCount = 1;
	ric_ie->RICData.statusCode = 0;
	ric_ie->RICData.Identifier = sme_qos_assign_dialog_token();
#ifndef USE_80211_WMMTSPEC_FOR_RIC
	ric_ie->TSPEC.present = 1;
	ric_ie->TSPEC.delay_bound = tspec_info->delay_bound;
	ric_ie->TSPEC.inactivity_int = tspec_info->inactivity_interval;
	ric_ie->TSPEC.burst_size = tspec_info->max_burst_size;
	ric_ie->TSPEC.max_msdu_size = tspec_info->maximum_msdu_size;
	ric_ie->TSPEC.max_service_int = tspec_info->max_service_interval;
	ric_ie->TSPEC.mean_data_rate = tspec_info->mean_data_rate;
	ric_ie->TSPEC.medium_time = 0;
	ric_ie->TSPEC.min_data_rate = tspec_info->min_data_rate;
	ric_ie->TSPEC.min_phy_rate = tspec_info->min_phy_rate;
	ric_ie->TSPEC.min_service_int = tspec_info->min_service_interval;
	ric_ie->TSPEC.size = tspec_info->nominal_msdu_size;
	ric_ie->TSPEC.peak_data_rate = tspec_info->peak_data_rate;
	ric_ie->TSPEC.surplus_bw_allowance = tspec_info->surplus_bw_allowance;
	ric_ie->TSPEC.suspension_int = tspec_info->suspension_interval;
	ric_ie->TSPEC.service_start_time = tspec_info->svc_start_time;
	ric_ie->TSPEC.direction = tspec_info->ts_info.direction;
	/* Make sure UAPSD is allowed */
	if (tspec_info->ts_info.psb)
		ric_ie->TSPEC.psb = tspec_info->ts_info.psb;
	else
		ric_ie->TSPEC.psb = 0;

	ric_ie->TSPEC.tsid = tspec_info->ts_info.tid;
	ric_ie->TSPEC.user_priority = tspec_info->ts_info.up;
	ric_ie->TSPEC.access_policy = SME_QOS_ACCESS_POLICY_EDCA;

	*ric_identifier = ric_ie->RICData.Identifier;

	status = dot11f_pack_ie_ric_data_desc(mac, ric_ie, ric_buffer,
					      sizeof(*ric_ie), ric_length);
	if (DOT11F_FAILED(status)) {
		sme_err("Packing of RIC Data of length %d failed with status %d",
			*ric_length, status);
	}
#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
	 */
	ric_ie->WMMTSPEC.present = 1;
	ric_ie->WMMTSPEC.version = 1;
	ric_ie->WMMTSPEC.delay_bound = tspec_info->delay_bound;
	ric_ie->WMMTSPEC.inactivity_int = tspec_info->inactivity_interval;
	ric_ie->WMMTSPEC.burst_size = tspec_info->max_burst_size;
	ric_ie->WMMTSPEC.max_msdu_size = tspec_info->maximum_msdu_size;
	ric_ie->WMMTSPEC.max_service_int = tspec_info->max_service_interval;
	ric_ie->WMMTSPEC.mean_data_rate = tspec_info->mean_data_rate;
	ric_ie->WMMTSPEC.medium_time = 0;
	ric_ie->WMMTSPEC.min_data_rate = tspec_info->min_data_rate;
	ric_ie->WMMTSPEC.min_phy_rate = tspec_info->min_phy_rate;
	ric_ie->WMMTSPEC.min_service_int = tspec_info->min_service_interval;
	ric_ie->WMMTSPEC.size = tspec_info->nominal_msdu_size;
	ric_ie->WMMTSPEC.peak_data_rate = tspec_info->peak_data_rate;
	ric_ie->WMMTSPEC.surplus_bw_allowance =
				tspec_info->surplus_bw_allowance;
	ric_ie->WMMTSPEC.suspension_int = tspec_info->suspension_interval;
	ric_ie->WMMTSPEC.service_start_time = tspec_info->svc_start_time;
	ric_ie->WMMTSPEC.direction = tspec_info->ts_info.direction;
	/* Make sure UAPSD is allowed */
	if (tspec_info->ts_info.psb)
		ric_ie->WMMTSPEC.psb = tspec_info->ts_info.psb;
	else
		ric_ie->WMMTSPEC.psb = 0;

	ric_ie->WMMTSPEC.tsid = tspec_info->ts_info.tid;
	ric_ie->WMMTSPEC.user_priority = tspec_info->ts_info.up;
	ric_ie->WMMTSPEC.access_policy = SME_QOS_ACCESS_POLICY_EDCA;

	status = dot11f_pack_ie_ric_data_desc(mac, ric_ie, ric_buffer,
					      sizeof(*ric_ie), ric_length);
	if (DOT11F_FAILED(status)) {
		sme_err("Packing of RIC Data of length %d failed with status %d",
			*ric_length, status);
	}
#endif /* 80211_TSPEC */
	*ric_identifier = ric_ie->RICData.Identifier;

	qdf_mem_free(ric_ie);
	return status;
}
#endif

/**
 * sme_qos_process_ft_reassoc_req_ev()- processes reassoc request
 *
 * @session_id: SME Session Id
 *
 * This function Process reassoc request related to QOS
 *
 * Return: QDF_STATUS enumeration value.
 */
static QDF_STATUS sme_qos_process_ft_reassoc_req_ev(
	uint8_t sessionId)
{
	struct sme_qos_sessioninfo *session;
	struct sme_qos_acinfo *ac_info;
	uint8_t ac, qos_requested = false;
	uint8_t tspec_index;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	tListElem *entry = NULL;

	sme_debug("Invoked on session %d", sessionId);

	session = &sme_qos_cb.sessionInfo[sessionId];

	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
		ac_info = &session->ac_info[ac];
		qos_requested = false;

		for (tspec_index = 0;
			tspec_index < SME_QOS_TSPEC_INDEX_MAX;
			tspec_index++) {
			/*
			 * Only in the below case, copy the AC's curr
			 * QoS Info to requested QoS info
			 */
			if ((ac_info->ricIdentifier[tspec_index]
				&& !ac_info->tspec_pending)
				|| (ac_info->
				tspec_mask_status & (1 << tspec_index))) {
				sme_debug("Copying the currentQos to requestedQos for AC=%d, flow=%d",
					  ac, tspec_index);

				ac_info->requested_QoSInfo[tspec_index] =
					ac_info->curr_QoSInfo[tspec_index];
				qdf_mem_zero(
					&ac_info->curr_QoSInfo[tspec_index],
					sizeof(struct sme_qos_wmmtspecinfo));
				qos_requested = true;
			}
		}

		/*
		 * Only if the tspec is required, transition the state to
		 * SME_QOS_REQUESTED for this AC
		 */
		if (qos_requested) {
			switch (ac_info->curr_state) {
			case SME_QOS_HANDOFF:
				sme_qos_state_transition(sessionId, ac,
					SME_QOS_REQUESTED);
				break;
			default:
				sme_err("FT Reassoc req event in unexpected state %d",
					ac_info->curr_state);
			}
		}
	}

	/*
	 * At this point of time, we are
	 * disconnected from the old AP, so it is safe
	 * to reset all these session variables
	 */
	session->apsdMask = 0;

	/*
	 * Now change reason and HO renewal of
	 * all the flow in this session only
	 */
	entry = csr_ll_peek_head(&sme_qos_cb.flow_list, false);
	if (!entry) {
		sme_debug("Flow List empty, nothing to update");
		return QDF_STATUS_E_FAILURE;
	}

	do {
		flow_info = GET_BASE_ADDR(entry, struct sme_qos_flowinfoentry,
					link);
		if (sessionId == flow_info->sessionId) {
			sme_debug("Changing FlowID %d reason to SETUP and HO renewal to false",
				  flow_info->QosFlowID);
			flow_info->reason = SME_QOS_REASON_SETUP;
			flow_info->hoRenewal = true;
		}
		entry = csr_ll_next(&sme_qos_cb.flow_list, entry, false);
	} while (entry);

	return QDF_STATUS_SUCCESS;
}

/**
 * sme_qos_fill_aggr_info - fill QOS Aggregation info
 *
 * @ac_id - index to the AC
 * @ts_id - index to TS for a given AC
 * @direction - traffic direction
 * @msg - QOS message
 * @session - sme session information
 *
 * this is a helper function to populate aggregation information
 * for QOS message.
 *
 * Return: None
 */
static void sme_qos_fill_aggr_info(int ac_id, int ts_id,
				   enum sme_qos_wmm_dir_type direction,
				   tSirAggrQosReq *msg,
				   struct sme_qos_sessioninfo *session)
{
	sme_debug("Found tspec entry AC=%d, flow=%d, direction = %d",
		  ac_id, ts_id, direction);

	msg->aggrInfo.aggrAddTsInfo[ac_id].dialogToken =
			sme_qos_assign_dialog_token();
	msg->aggrInfo.aggrAddTsInfo[ac_id].lleTspecPresent =
		session->ac_info[ac_id].addTsRsp[ts_id].rsp.lleTspecPresent;
	msg->aggrInfo.aggrAddTsInfo[ac_id].numTclas =
		session->ac_info[ac_id].addTsRsp[ts_id].rsp.numTclas;
	qdf_mem_copy(msg->aggrInfo.aggrAddTsInfo[ac_id].tclasInfo,
		     session->ac_info[ac_id].addTsRsp[ts_id].rsp.tclasInfo,
		     SIR_MAC_TCLASIE_MAXNUM);
	msg->aggrInfo.aggrAddTsInfo[ac_id].tclasProc =
		session->ac_info[ac_id].addTsRsp[ts_id].rsp.tclasProc;
	msg->aggrInfo.aggrAddTsInfo[ac_id].tclasProcPresent =
		session->ac_info[ac_id].addTsRsp[ts_id].rsp.tclasProcPresent;
	msg->aggrInfo.aggrAddTsInfo[ac_id].tspec =
		session->ac_info[ac_id].addTsRsp[ts_id].rsp.tspec;
	msg->aggrInfo.aggrAddTsInfo[ac_id].wmeTspecPresent =
		session->ac_info[ac_id].addTsRsp[ts_id].rsp.wmeTspecPresent;
	msg->aggrInfo.aggrAddTsInfo[ac_id].wsmTspecPresent =
		session->ac_info[ac_id].addTsRsp[ts_id].rsp.wsmTspecPresent;
	msg->aggrInfo.tspecIdx |= (1 << ac_id);

	/* Mark the index for this AC as pending for response, which would be */
	/* used to validate the AddTS response from HAL->PE->SME */
	session->ac_info[ac_id].tspec_pending = (1 << ts_id);

}

/**
 * sme_qos_ft_aggr_qos_req - send aggregated QOS request
 *
 * @mac_ctx - global MAC context
 * @session_id - sme session Id
 *
 * This function is used to send aggregated QOS request to HAL.
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS sme_qos_ft_aggr_qos_req(struct mac_context *mac_ctx, uint8_t
					session_id)
{
	tSirAggrQosReq *aggr_req = NULL;
	struct sme_qos_sessioninfo *session;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;
	int i, j = 0;
	uint8_t direction;

	sme_debug("invoked on session %d", session_id);

	session = &sme_qos_cb.sessionInfo[session_id];

	aggr_req = qdf_mem_malloc(sizeof(tSirAggrQosReq));
	if (!aggr_req)
		return QDF_STATUS_E_NOMEM;

	aggr_req->messageType = eWNI_SME_FT_AGGR_QOS_REQ;
	aggr_req->length = sizeof(tSirAggrQosReq);
	aggr_req->sessionId = session_id;
	aggr_req->timeout = 0;
	aggr_req->rspReqd = true;
	qdf_mem_copy(&aggr_req->bssid.bytes[0],
		     &session->assocInfo.bss_desc->bssId[0],
		     sizeof(struct qdf_mac_addr));

	for (i = 0; i < QCA_WLAN_AC_ALL; i++) {
		for (j = 0; j < SME_QOS_TSPEC_INDEX_MAX; j++) {
			sme_debug("ac=%d, tspec_mask_status=%x, tspec_index=%d direction = %d",
				  i, session->ac_info[i].tspec_mask_status, j,
				  session->ac_info[i].addTsRsp[j].rsp.tspec.
				  tsinfo.traffic.direction);
			/* Check if any flow is active on this AC */
			if (!((session->ac_info[i].tspec_mask_status) &
			     (1 << j)))
				continue;

			direction = session->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))
				sme_qos_fill_aggr_info(i, j, direction,
						aggr_req, session);
		}
	}

	sme_debug("Sending aggregated message to HAL 0x%x",
		  aggr_req->aggrInfo.tspecIdx);

	if (QDF_IS_STATUS_SUCCESS(umac_send_mb_message_to_mac(aggr_req))) {
		status = QDF_STATUS_SUCCESS;
		sme_info("sent down a AGGR QoS req to PE");
	}

	return status;
}

static
QDF_STATUS sme_qos_process_ftric_response(struct mac_context *mac,
					  uint8_t sessionId,
					  tDot11fIERICDataDesc *pRicDataDesc,
					  uint8_t ac, uint8_t tspecIndex)
{
	uint8_t i = 0;
	tpSirAddtsRsp pAddtsRsp = &sme_qos_cb.sessionInfo[sessionId].
					ac_info[ac].addTsRsp[tspecIndex];

	qdf_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
		 */
		convert_tspec(mac, &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 */
			convert_tclas(mac, &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 */
		convert_schedule(mac, &pAddtsRsp->rsp.schedule,
				 &pRicDataDesc->Schedule);
	}
	/* Need to check the below portion is a part of WMM TSPEC */
	/* Process Delay element */
	if (pRicDataDesc->TSDelay.present)
		convert_ts_delay(mac, &pAddtsRsp->rsp.delay,
				 &pRicDataDesc->TSDelay);

	/* Need to call for WMMTSPEC */
	if (pRicDataDesc->WMMTSPEC.present)
		convert_wmmtspec(mac, &pAddtsRsp->rsp.tspec,
				 &pRicDataDesc->WMMTSPEC);

	/* return sme_qos_process_add_ts_rsp(mac, &addtsRsp); */
	return QDF_STATUS_SUCCESS;
}

/**
 * sme_qos_process_aggr_qos_rsp - process qos aggregation response
 *
 * @mac_ctx - global mac context
 * @msgbuf - SME message buffer
 *
 * this function process the QOS aggregation response received.
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS sme_qos_process_aggr_qos_rsp(struct mac_context *mac_ctx,
					void *msgbuf)
{
	tpSirAggrQosRsp rsp = (tpSirAggrQosRsp) msgbuf;
	tSirAddtsRsp addtsrsp;
	QDF_STATUS status = QDF_STATUS_SUCCESS;
	int i, j = 0;
	uint8_t sessionid = rsp->sessionId;

	sme_debug("Received AGGR_QOS resp from LIM");

	/* Copy the updated response information for TSPEC of all the ACs */
	for (i = 0; i < QCA_WLAN_AC_ALL; i++) {
		uint8_t tspec_mask_status =
			sme_qos_cb.sessionInfo[sessionid].ac_info[i].
			tspec_mask_status;
		for (j = 0; j < SME_QOS_TSPEC_INDEX_MAX; j++) {
			uint8_t direction =
				sme_qos_cb.sessionInfo[sessionid].
				ac_info[i].addTsRsp[j].rsp.tspec.tsinfo.traffic.
				direction;

			sme_debug("Addts rsp from LIM AC=%d, flow=%d dir=%d, tspecIdx=%x",
				  i, j, direction, rsp->aggrInfo.tspecIdx);

			/* Check if the direction is Uplink or bi-directional */
			if (!(((1 << i) & rsp->aggrInfo.tspecIdx) &&
			    ((tspec_mask_status) & (1 << j)) &&
			    ((direction == SME_QOS_WMM_TS_DIR_UPLINK) ||
			     (direction == SME_QOS_WMM_TS_DIR_BOTH)))) {
				continue;
			}
			addtsrsp =
				sme_qos_cb.sessionInfo[sessionid].ac_info[i].
				addTsRsp[j];
			addtsrsp.rc = rsp->aggrInfo.aggrRsp[i].status;
			addtsrsp.rsp.status = rsp->aggrInfo.aggrRsp[i].status;
			addtsrsp.rsp.tspec = rsp->aggrInfo.aggrRsp[i].tspec;

			sme_debug("Processing Addts rsp from LIM AC=%d, flow=%d",
				  i, j);
			/* post ADD TS response for each */
			if (sme_qos_process_add_ts_rsp(mac_ctx, &addtsrsp) !=
			    QDF_STATUS_SUCCESS)
				status = QDF_STATUS_E_FAILURE;
		}
	}
	return status;
}

/**
 * sme_qos_find_matching_tspec() - utility function to find matching tspec
 * @mac_ctx: global MAC context
 * @sessionid: session ID
 * @ac: AC index
 * @ac_info: Current AC info
 * @ric_data_desc: pointer to ric data
 * @ric_rsplen: pointer to ric response length
 *
 * This utility function is called by sme_qos_process_ft_reassoc_rsp_ev
 * to find the matching tspec
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS sme_qos_find_matching_tspec(struct mac_context *mac_ctx,
		uint8_t sessionid, uint8_t ac, struct sme_qos_acinfo *ac_info,
		tDot11fIERICDataDesc *ric_data_desc, uint32_t *ric_rsplen)
{
	uint8_t tspec_flow_index;
	QDF_STATUS status = QDF_STATUS_SUCCESS;

	sme_debug("invoked on session %d", sessionid);

	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 (!ac_info->ricIdentifier[tspec_flow_index])
			continue;

		if (!*ric_rsplen) {
			sme_err("RIC Response not received for AC %d on TSPEC Index %d, RIC Req Identifier = %d",
				ac, tspec_flow_index,
				ac_info->ricIdentifier[tspec_flow_index]);
			continue;
		}
		/* Now we got response for this identifier. Process it. */
		if (!ric_data_desc->present)
			continue;
		if (!ric_data_desc->RICData.present)
			continue;

		if (ric_data_desc->RICData.Identifier !=
			ac_info->ricIdentifier[tspec_flow_index]) {
			sme_err("RIC response order not same as request sent. Request ID = %d, Response ID = %d",
				ac_info->ricIdentifier[tspec_flow_index],
				ric_data_desc->RICData.Identifier);
		} else {
			sme_debug("Processing RIC Response for AC %d, TSPEC Flow index %d with RIC ID %d",
				  ac, tspec_flow_index,
				  ric_data_desc->RICData.Identifier);
			status = sme_qos_process_ftric_response(mac_ctx,
					sessionid, ric_data_desc, ac,
					tspec_flow_index);
			if (QDF_STATUS_SUCCESS != status) {
				sme_err("Failed with status %d for AC %d in TSPEC Flow index = %d",
					status, ac, tspec_flow_index);
			}
		}
		ric_data_desc++;
		*ric_rsplen -= sizeof(tDot11fIERICDataDesc);
	}
	return status;
}

#ifdef WLAN_FEATURE_ROAM_OFFLOAD
/**
 * sme_qos_find_matching_tspec_lfr3() - utility function to find matching tspec
 * @mac_ctx: global MAC context
 * @sessionid: session ID
 * @ac: AC index
 * @qos_session: QOS session
 * @ric_data_desc: pointer to ric data
 * @ric_rsplen: ric response length
 *
 * This utility function is called by sme_qos_process_ft_reassoc_rsp_ev
 * to find the matching tspec while LFR3 is enabled.
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS sme_qos_find_matching_tspec_lfr3(struct mac_context *mac_ctx,
		uint8_t sessionid, uint8_t ac, struct sme_qos_sessioninfo
		*qos_session,
		tDot11fIERICDataDesc *ric_data_desc, uint32_t ric_rsplen)
{
	struct sme_qos_acinfo *ac_info;
	uint8_t tspec_flow_idx;
	bool found = false;
	enum sme_qos_wmm_dir_type direction, qos_dir;
	uint8_t ac1;
	tDot11fIERICDataDesc *ric_data = NULL;
	uint32_t ric_len;
	QDF_STATUS status = QDF_STATUS_SUCCESS;

	sme_debug("invoked on session %d", sessionid);

	if (ac == QCA_WLAN_AC_ALL) {
		sme_err("Invalid AC %d", ac);
		return QDF_STATUS_E_FAILURE;
	}
	ric_data = ric_data_desc;
	ric_len = ric_rsplen;
	ac_info = &qos_session->ac_info[ac];
	for (tspec_flow_idx = 0; tspec_flow_idx < SME_QOS_TSPEC_INDEX_MAX;
	     tspec_flow_idx++) {
		if (!((qos_session->ac_info[ac].tspec_mask_status) &
		    (1 << tspec_flow_idx)))
			goto sme_qos_next_ric;
		qos_dir =
		  ac_info->requested_QoSInfo[tspec_flow_idx].ts_info.direction;
		do {
			ac1 = sme_qos_up_to_ac(
				ric_data->WMMTSPEC.user_priority);
			if (ac1 == QCA_WLAN_AC_ALL) {
				sme_err("Invalid AC %d UP %d", ac1,
					ric_data->WMMTSPEC.user_priority);
				break;
			}
			direction = ric_data->WMMTSPEC.direction;
			if (ac == ac1 && direction == qos_dir) {
				found = true;
				status = sme_qos_process_ftric_response(mac_ctx,
						sessionid, ric_data, ac,
						tspec_flow_idx);
				if (QDF_STATUS_SUCCESS != status) {
					sme_err("Failed with status %d for AC %d in TSPEC Flow index = %d",
						status, ac, tspec_flow_idx);
				}
				break;
			}
			ric_data++;
			ric_len -= sizeof(tDot11fIERICDataDesc);
		} while (ric_len);
sme_qos_next_ric:
		ric_data = ric_data_desc;
		ric_len = ric_rsplen;
		found = false;
	}

	return status;
}
#endif /* WLAN_FEATURE_ROAM_OFFLOAD */

static
QDF_STATUS sme_qos_process_ft_reassoc_rsp_ev(struct mac_context *mac_ctx,
				uint8_t sessionid, void *event_info)
{
	struct sme_qos_sessioninfo *qos_session;
	struct sme_qos_acinfo *ac_info;
	uint8_t ac;
	tDot11fIERICDataDesc *ric_data_desc = NULL;
	QDF_STATUS status = QDF_STATUS_SUCCESS;
	struct csr_roam_session *csr_session = CSR_GET_SESSION(mac_ctx,
				sessionid);
	struct csr_roam_connectedinfo *csr_conn_info = NULL;
	uint32_t ric_rsplen;
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
	tDot11fIERICDataDesc *ric_data = NULL;
	uint32_t ric_len;
#endif

	if (!csr_session) {
		sme_err("The Session pointer is NULL");
		return QDF_STATUS_E_FAILURE;
	}
	csr_conn_info = &csr_session->connectedInfo;
	ric_rsplen = csr_conn_info->nRICRspLength;

	sme_debug("invoked on session %d", sessionid);

	qos_session = &sme_qos_cb.sessionInfo[sessionid];

	ric_data_desc = (tDot11fIERICDataDesc *) ((csr_conn_info->pbFrames) +
				(csr_conn_info->nBeaconLength +
				 csr_conn_info->nAssocReqLength +
				 csr_conn_info->nAssocRspLength));

	if (!wlan_cm_is_roam_sync_in_progress(mac_ctx->psoc, sessionid)) {
		for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
			ac_info = &qos_session->ac_info[ac];
			sme_qos_find_matching_tspec(mac_ctx, sessionid, ac,
					ac_info, ric_data_desc, &ric_rsplen);
		}

		if (ric_rsplen) {
			sme_err("RIC Resp still follows . Rem len = %d",
				ric_rsplen);
		}
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
	} else {
		sme_debug("LFR3-11r Compare RIC in Reassoc Resp to find matching tspec in host");
		ric_data = ric_data_desc;
		ric_len = ric_rsplen;
		if (ric_rsplen && ric_data_desc->present &&
		    ric_data_desc->WMMTSPEC.present) {
			for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL;
			     ac++) {
				sme_qos_find_matching_tspec_lfr3(mac_ctx,
					sessionid, ac, qos_session, ric_data,
					ric_len);
			}
		} else
			sme_debug("LFR3-11r ric_rsplen is zero or ric_data_desc is not present or wmmtspec is not present");
#endif
	}

	/* Send the Aggregated QoS request to HAL */
	status = sme_qos_ft_aggr_qos_req(mac_ctx, sessionid);

	return status;
}

#ifdef WLAN_FEATURE_MSCS
void sme_send_mscs_action_frame(uint8_t vdev_id)
{
	struct mscs_req_info *mscs_req;
	struct sme_qos_sessioninfo *qos_session;
	struct scheduler_msg msg = {0};
	QDF_STATUS qdf_status;

	qos_session = &sme_qos_cb.sessionInfo[vdev_id];
	if (!qos_session) {
		sme_debug("qos_session is NULL");
		return;
	}
	mscs_req = qdf_mem_malloc(sizeof(*mscs_req));
	if (!mscs_req)
		return;

	mscs_req->vdev_id = vdev_id;
	if (!qos_session->assocInfo.bss_desc) {
		sme_err("BSS descriptor is NULL so we won't send request to PE");
		qdf_mem_free(mscs_req);
		return;
	}
	qdf_mem_copy(&mscs_req->bssid.bytes[0],
		     &qos_session->assocInfo.bss_desc->bssId[0],
		     sizeof(struct qdf_mac_addr));

	mscs_req->dialog_token = sme_qos_assign_dialog_token();
	mscs_req->dec.request_type = SCS_REQ_ADD;
	mscs_req->dec.user_priority_control = MSCS_USER_PRIORITY;
	mscs_req->dec.stream_timeout = (MSCS_STREAM_TIMEOUT * 1000);
	mscs_req->dec.tclas_mask.classifier_type = MSCS_TCLAS_CLASSIFIER_TYPE;
	mscs_req->dec.tclas_mask.classifier_mask = MSCS_TCLAS_CLASSIFIER_MASK;

	msg.type = eWNI_SME_MSCS_REQ;
	msg.reserved = 0;
	msg.bodyptr = mscs_req;
	qdf_status = scheduler_post_message(QDF_MODULE_ID_SME, QDF_MODULE_ID_PE,
					    QDF_MODULE_ID_PE, &msg);
	if (QDF_IS_STATUS_ERROR(qdf_status)) {
		sme_err("Fail to send mscs request to PE");
		qdf_mem_free(mscs_req);
	}
}
#endif

/**
 * sme_qos_add_ts_req() - send ADDTS request.
 * @mac: Pointer to the global MAC parameter structure.
 * @sessionId: Session upon which the TSPEC should be added
 * @pTspec_Info: Pointer to struct sme_qos_wmmtspecinfo which contains the WMM
 *               TSPEC related info as defined above
 * @ac: Enumeration of the various EDCA Access Categories.
 *
 * This function is used to send down the ADDTS request with TSPEC params to PE
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS sme_qos_add_ts_req(struct mac_context *mac,
			      uint8_t sessionId,
			      struct sme_qos_wmmtspecinfo *pTspec_Info,
			      enum qca_wlan_ac_type ac)
{
	tSirAddtsReq *pMsg = NULL;
	struct sme_qos_sessioninfo *pSession;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;
#ifdef FEATURE_WLAN_DIAG_SUPPORT
	WLAN_HOST_DIAG_EVENT_DEF(qos, host_event_wlan_qos_payload_type);
#endif
	sme_debug("invoked on session %d for AC %d", sessionId, ac);
	if (sessionId >= WLAN_MAX_VDEVS) {
		/* err msg */
		sme_err("sessionId(%d) is invalid", sessionId);
		return QDF_STATUS_E_FAILURE;
	}

	pSession = &sme_qos_cb.sessionInfo[sessionId];
	pMsg = qdf_mem_malloc(sizeof(tSirAddtsReq));
	if (!pMsg)
		return QDF_STATUS_E_NOMEM;

	pMsg->messageType = eWNI_SME_ADDTS_REQ;
	pMsg->length = sizeof(tSirAddtsReq);
	pMsg->sessionId = sessionId;
	pMsg->timeout = 0;
	pMsg->rspReqd = true;
	pMsg->req.dialogToken = sme_qos_assign_dialog_token();
	/* 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 */
	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 (!pSession->assocInfo.bss_desc) {
		sme_err("BSS descriptor is NULL so we don't send request to PE");
		qdf_mem_free(pMsg);
		return QDF_STATUS_E_FAILURE;
	}
	qdf_mem_copy(&pMsg->bssid.bytes[0],
		     &pSession->assocInfo.bss_desc->bssId[0],
		     sizeof(struct qdf_mac_addr));
	sme_debug("up = %d, tid = %d",
		  pTspec_Info->ts_info.up, pTspec_Info->ts_info.tid);
#ifdef FEATURE_WLAN_ESE
	if (wlan_cm_get_ese_assoc(mac->pdev, sessionId)) {
		pMsg->req.tsrsIE.tsid = pTspec_Info->ts_info.up;
		pMsg->req.tsrsPresent = 1;
	}
#endif
	if (QDF_IS_STATUS_SUCCESS(umac_send_mb_message_to_mac(pMsg))) {
		status = QDF_STATUS_SUCCESS;
		sme_debug("sent down a ADDTS req to PE");
		/* event: EVENT_WLAN_QOS */
#ifdef FEATURE_WLAN_DIAG_SUPPORT
		qos.eventId = SME_QOS_DIAG_ADDTS_REQ;
		qos.reasonCode = SME_QOS_DIAG_USER_REQUESTED;
		WLAN_HOST_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS);
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
	}
	return status;
}

/*
 * sme_qos_del_ts_req() - To send down the DELTS request with TSPEC params
 *  to PE
 *
 * mac - Pointer to the global MAC parameter structure.
 * sessionId - Session from which the TSPEC should be deleted
 * ac - Enumeration of the various EDCA Access Categories.
 * tspec_mask - on which tspec per AC, the delts is requested
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_del_ts_req(struct mac_context *mac,
			      uint8_t sessionId,
			      enum qca_wlan_ac_type ac, uint8_t tspec_mask)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	tSirDeltsReq *pMsg;
	struct sme_qos_wmmtspecinfo *pTspecInfo;
	struct mac_ts_info tsinfo;

#ifdef FEATURE_WLAN_DIAG_SUPPORT
	WLAN_HOST_DIAG_EVENT_DEF(qos, host_event_wlan_qos_payload_type);
#endif
	sme_debug("invoked on session %d for AC %d", sessionId, ac);
	pMsg = qdf_mem_malloc(sizeof(tSirDeltsReq));
	if (!pMsg)
		return QDF_STATUS_E_NOMEM;

	/* get pointer to the TSPEC being deleted */
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	pACInfo = &pSession->ac_info[ac];
	pTspecInfo = &pACInfo->curr_QoSInfo[tspec_mask - 1];
	pMsg->messageType = eWNI_SME_DELTS_REQ;
	pMsg->length = sizeof(tSirDeltsReq);
	pMsg->sessionId = sessionId;
	pMsg->rspReqd = 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 (!pSession->assocInfo.bss_desc) {
		sme_err("BSS descriptor is NULL so we don't send request to PE");
		qdf_mem_free(pMsg);
		return QDF_STATUS_E_FAILURE;
	}
	qdf_mem_copy(&pMsg->bssid.bytes[0],
		     &pSession->assocInfo.bss_desc->bssId[0],
		     sizeof(struct qdf_mac_addr));

	sme_debug("up = %d, tid = %d",
		  pTspecInfo->ts_info.up, pTspecInfo->ts_info.tid);
	qdf_mem_zero(&pACInfo->curr_QoSInfo[tspec_mask - 1],
		     sizeof(struct sme_qos_wmmtspecinfo));
	qdf_mem_copy(&tsinfo, &pMsg->req.tspec.tsinfo,
		     sizeof(struct mac_ts_info));

	if (!QDF_IS_STATUS_SUCCESS(umac_send_mb_message_to_mac(pMsg))) {
		sme_err("DELTS req to PE failed");
		return QDF_STATUS_E_FAILURE;
	}

	sme_debug("sent down a DELTS req to PE");
#ifdef FEATURE_WLAN_DIAG_SUPPORT
	qos.eventId = SME_QOS_DIAG_DELTS;
	qos.reasonCode = SME_QOS_DIAG_USER_REQUESTED;
	WLAN_HOST_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS);
#endif

	sme_set_tspec_uapsd_mask_per_session(mac, &tsinfo, sessionId);

	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_process_add_ts_rsp() - Function to process the
 * eWNI_SME_ADDTS_RSP came from PE
 *
 * @mac - Pointer to the global MAC parameter structure.
 * @msg_buf - Pointer to the msg buffer came from PE.
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_process_add_ts_rsp(struct mac_context *mac,
					     void *msg_buf)
{
	tpSirAddtsRsp paddts_rsp = (tpSirAddtsRsp) msg_buf;
	struct sme_qos_sessioninfo *pSession;
	uint8_t sessionId = paddts_rsp->sessionId;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;
	enum sme_qos_wmmuptype up =
		(enum sme_qos_wmmuptype)
		paddts_rsp->rsp.tspec.tsinfo.traffic.userPrio;
	struct sme_qos_acinfo *pACInfo;
	enum qca_wlan_ac_type ac;
#ifdef FEATURE_WLAN_DIAG_SUPPORT
	WLAN_HOST_DIAG_EVENT_DEF(qos, host_event_wlan_qos_payload_type);
#endif

	pSession = &sme_qos_cb.sessionInfo[sessionId];

	sme_debug("invoked on session %d for UP %d", sessionId, up);

	ac = sme_qos_up_to_ac(up);
	if (QCA_WLAN_AC_ALL == ac) {
		/* err msg */
		sme_err("invalid AC %d from UP %d", ac, up);

		return QDF_STATUS_E_FAILURE;
	}
	pACInfo = &pSession->ac_info[ac];
	if (SME_QOS_HANDOFF == pACInfo->curr_state) {
		sme_debug("ADDTS Rsp received for AC %d in HANDOFF State. Dropping",
			  ac);
		return QDF_STATUS_SUCCESS;
	}

	sme_debug("Invoked on session %d with return code %d",
		  sessionId, paddts_rsp->rc);
	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_HOST_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS);
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
		status =
			sme_qos_process_add_ts_failure_rsp(mac, sessionId,
							   &paddts_rsp->rsp);
	} else {
		status =
			sme_qos_process_add_ts_success_rsp(mac, sessionId,
							   &paddts_rsp->rsp);
	}
	return status;
}

/*
 * sme_qos_process_del_ts_rsp() - Function to process the
 *  eWNI_SME_DELTS_RSP came from PE
 *
 * mac - Pointer to the global MAC parameter structure.
 * msg_buf - Pointer to the msg buffer came from PE.
 *
 * Return QDF_STATUS
 */
static
QDF_STATUS sme_qos_process_del_ts_rsp(struct mac_context *mac, void *msg_buf)
{
	tpSirDeltsRsp pDeltsRsp = (tpSirDeltsRsp) msg_buf;
	struct sme_qos_sessioninfo *pSession;
	uint8_t sessionId = pDeltsRsp->sessionId;

	/* msg */
	sme_debug("Invoked on session %d with return code %d",
		  sessionId, pDeltsRsp->rc);
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	(void)sme_qos_process_buffered_cmd(sessionId);
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_process_del_ts_ind() - 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
 *
 * mac - Pointer to the global MAC parameter structure.
 * msg_buf - Pointer to the msg buffer came from PE.
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_process_del_ts_ind(struct mac_context *mac,
					     void *msg_buf)
{
	tpSirDeltsRsp pdeltsind = (tpSirDeltsRsp)msg_buf;
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	uint8_t sessionId = pdeltsind->sessionId;
	enum qca_wlan_ac_type ac;
	struct sme_qos_searchinfo search_key;
	struct mac_ts_info *tsinfo;
	enum sme_qos_wmmuptype up =
		(enum sme_qos_wmmuptype)
		pdeltsind->rsp.tspec.tsinfo.traffic.userPrio;
#ifdef FEATURE_WLAN_DIAG_SUPPORT
	WLAN_HOST_DIAG_EVENT_DEF(qos, host_event_wlan_qos_payload_type);
#endif
	sme_debug("Invoked on session %d for UP %d", sessionId, up);
	tsinfo = &pdeltsind->rsp.tspec.tsinfo;
	ac = sme_qos_up_to_ac(up);
	if (QCA_WLAN_AC_ALL == ac) {
		/* err msg */
		sme_err("invalid AC %d from UP %d", ac, up);
		return QDF_STATUS_E_FAILURE;
	}
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	pACInfo = &pSession->ac_info[ac];

	qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo));
	/* 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 perticular AC & delete them, also send HDD
	 * indication through the callback it registered per request
	 */
	if (!QDF_IS_STATUS_SUCCESS
		    (sme_qos_find_all_in_flow_list(mac, search_key,
						sme_qos_del_ts_ind_fnp))) {
		sme_err("no match found for ac = %d", search_key.key.ac_type);
		return QDF_STATUS_E_FAILURE;
	}
	sme_set_tspec_uapsd_mask_per_session(mac, tsinfo, sessionId);
/* 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_HOST_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS);
#endif /* FEATURE_WLAN_DIAG_SUPPORT */

	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_process_assoc_complete_ev() - Function to process the
 *  SME_QOS_CSR_ASSOC_COMPLETE event indication from CSR
 *
 * pEvent_info - Pointer to relevant info from CSR.
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_process_assoc_complete_ev(struct mac_context *mac, uint8_t
						sessionId, void *pEvent_info)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;
	enum qca_wlan_ac_type ac = QCA_WLAN_AC_BE;

	sme_debug("invoked on session %d", sessionId);
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	if (((SME_QOS_INIT == pSession->ac_info[QCA_WLAN_AC_BE].curr_state)
	     && (SME_QOS_INIT ==
		 pSession->ac_info[QCA_WLAN_AC_BK].curr_state)
	     && (SME_QOS_INIT ==
		 pSession->ac_info[QCA_WLAN_AC_VI].curr_state)
	     && (SME_QOS_INIT ==
		 pSession->ac_info[QCA_WLAN_AC_VO].curr_state))
	    || (pSession->handoffRequested)) {
		/* get the association info */
		if (!pEvent_info) {
			/* err msg */
			sme_err("pEvent_info is NULL");
			return status;
		}
		if (!((sme_QosAssocInfo *)pEvent_info)->bss_desc) {
			/* err msg */
			sme_err("bss_desc is NULL");
			return status;
		}
		if ((pSession->assocInfo.bss_desc) &&
		    (csr_is_bssid_match
			     ((struct qdf_mac_addr *)
					&pSession->assocInfo.bss_desc->bssId,
			     (struct qdf_mac_addr *) &(((sme_QosAssocInfo *)
					pEvent_info)->bss_desc->bssId)))) {
			sme_err("assoc with the same BSS, no update needed");
		} else
			status = sme_qos_save_assoc_info(pSession, pEvent_info);
	} else {
		sme_err("wrong state: BE %d, BK %d, VI %d, VO %d",
			pSession->ac_info[QCA_WLAN_AC_BE].curr_state,
			pSession->ac_info[QCA_WLAN_AC_BK].curr_state,
			pSession->ac_info[QCA_WLAN_AC_VI].curr_state,
			pSession->ac_info[QCA_WLAN_AC_VO].curr_state);
		return status;
	}
	/* the session is active */
	pSession->sessionActive = true;
	if (pSession->handoffRequested) {
		pSession->handoffRequested = false;
		/* renew all flows */
		(void)sme_qos_process_buffered_cmd(sessionId);
		status = QDF_STATUS_SUCCESS;
	} else {
		for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
			pACInfo = &pSession->ac_info[ac];
			switch (pACInfo->curr_state) {
			case SME_QOS_INIT:
				sme_qos_state_transition(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:
				sme_err("On session %d AC %d is in wrong state %d",
					sessionId, ac,
					pACInfo->curr_state);
				break;
			}
		}
	}
	return status;
}

/*
 * sme_qos_process_reassoc_req_ev() - Function to process the
 *  SME_QOS_CSR_REASSOC_REQ event indication from CSR
 *
 * pEvent_info - Pointer to relevant info from CSR.
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_process_reassoc_req_ev(struct mac_context *mac, uint8_t
						sessionId, void *pEvent_info)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	enum qca_wlan_ac_type ac;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	tListElem *entry = NULL;

	sme_debug("invoked on session %d", sessionId);
	pSession = &sme_qos_cb.sessionInfo[sessionId];

	if (pSession->ftHandoffInProgress) {
		sme_debug("no need for state transition, should already be in handoff state");
		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)) {
			sme_err("curr_state is not HANDOFF, session %d",
				sessionId);
			return QDF_STATUS_E_FAILURE;
		}
		sme_qos_process_ft_reassoc_req_ev(sessionId);
		return QDF_STATUS_SUCCESS;
	}

	if (pSession->handoffRequested) {
		sme_debug("no need for state transition, should already be in handoff state");

		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)) {
			sme_err("curr_state is not HANDOFF, session %d",
				sessionId);
			return QDF_STATUS_E_FAILURE;
		}

		/*
		 * Now change reason and HO renewal of
		 * all the flow in this session only
		 */
		entry = csr_ll_peek_head(&sme_qos_cb.flow_list, false);
		if (!entry) {
			sme_debug("Flow List empty, nothing to update");
			return QDF_STATUS_E_FAILURE;
		}

		do {
			flow_info = GET_BASE_ADDR(entry, struct sme_qos_flowinfoentry,
						  link);
			if (sessionId == flow_info->sessionId) {
				sme_info("Changing FlowID %d reason toSETUP and HO renewal to true",
					 flow_info->QosFlowID);
				flow_info->reason = SME_QOS_REASON_SETUP;
				flow_info->hoRenewal = true;
			}
			entry = csr_ll_next(&sme_qos_cb.flow_list, entry,
					    false);
		} while (entry);

		/* buffer the existing flows to be renewed after handoff is
		 * done
		 */
		sme_qos_buffer_existing_flows(mac, sessionId);
		/* clean up the control block partially for handoff */
		sme_qos_cleanup_ctrl_blk_for_handoff(mac, sessionId);
		return QDF_STATUS_SUCCESS;
	}
/* TBH: Assuming both handoff algo & 11r willn't be enabled at the same time */
	if (pSession->ftHandoffInProgress) {
		sme_debug("no need for state transition, should already be in handoff state");

		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)) {
			sme_err("curr_state is not HANDOFF, session %d",
				sessionId);
			return QDF_STATUS_E_FAILURE;
		}

		sme_qos_process_ft_reassoc_req_ev(sessionId);
		return QDF_STATUS_SUCCESS;
	}

	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; 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_qos_state_transition(sessionId, ac,
						SME_QOS_HANDOFF);
			break;
		case SME_QOS_HANDOFF:
			/* This is normal because sme_qos_request_reassoc may
			 * already change the state
			 */
			break;
		case SME_QOS_CLOSED:
		case SME_QOS_INIT:
		default:
			sme_err("On session %d AC %d is in wrong state %d",
				sessionId, ac, pACInfo->curr_state);
			break;
		}
	}
	return QDF_STATUS_SUCCESS;
}

/**
 * sme_qos_handle_handoff_state() - utility function called by
 *                                  sme_qos_process_reassoc_success_ev
 * @mac_ctx: global MAC context
 * @qos_session: QOS session
 * @ac_info: AC information
 * @ac: current AC index
 * @sessionid: session id
 *
 * This function is called by sme_qos_process_reassoc_success_ev
 * to update the state machine on the reception of reassoc success
 * notification
 *
 * Return: QDF_STATUS
 */
static
QDF_STATUS sme_qos_handle_handoff_state(struct mac_context *mac_ctx,
		struct sme_qos_sessioninfo *qos_session,
		struct sme_qos_acinfo *ac_info,
		enum qca_wlan_ac_type ac, uint8_t sessionid)

{
	struct sme_qos_searchinfo search_key;
	struct sme_qos_searchinfo search_key1;
	enum qca_wlan_ac_type ac_index;
	tListElem *list_elt = NULL;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;

	/* return to our previous state */
	sme_qos_state_transition(sessionid, ac, ac_info->prev_state);
	/* for which ac APSD (hence the reassoc) is requested */
	if (!ac_info->reassoc_pending)
		return QDF_STATUS_SUCCESS;

	/*
	 * update the apsd mask in CB - make sure to take care of the
	 * case where we are resetting the bit in apsd_mask
	 */
	if (ac_info->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0].ts_info.psb)
		qos_session->apsdMask |= 1 << (QCA_WLAN_AC_VO - ac);
	else
		qos_session->apsdMask &= ~(1 << (QCA_WLAN_AC_VO - ac));

	ac_info->reassoc_pending = false;
	/*
	 * during setup it gets set as addts & reassoc both gets a
	 * pending flag ac_info->tspec_pending = 0;
	 */
	sme_qos_state_transition(sessionid, ac, SME_QOS_QOS_ON);
	/* notify HDD with new Service Interval */
	ac_info->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0] =
		ac_info->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0];
	qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo));
	/* 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?? */

	qdf_mem_zero(&search_key1, sizeof(struct sme_qos_searchinfo));
	/* 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 = QCA_WLAN_AC_BE; ac_index < QCA_WLAN_AC_ALL;
	     ac_index++) {
		list_elt = sme_qos_find_in_flow_list(search_key1);
		if (list_elt) {
			flow_info = GET_BASE_ADDR(list_elt,
					struct sme_qos_flowinfoentry, link);
			if (flow_info->ac_type == ac) {
				ac_info->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
	 */
	status = sme_qos_find_all_in_flow_list(mac_ctx, search_key,
			sme_qos_reassoc_success_ev_fnp);
	if (!QDF_IS_STATUS_SUCCESS(status)) {
		sme_err("no match found for ac = %d", search_key.key.ac_type);
		return QDF_STATUS_E_FAILURE;
	}
	ac_info->hoRenewal = false;
	qdf_mem_zero(&ac_info->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0],
		     sizeof(struct sme_qos_wmmtspecinfo));

	return status;
}

/**
 * sme_qos_process_reassoc_success_ev() - process SME_QOS_CSR_REASSOC_COMPLETE
 *
 * @mac_ctx: global MAC context
 * @sessionid: session ID
 * @event_info: event buffer from CSR
 *
 * Function to process the SME_QOS_CSR_REASSOC_COMPLETE event indication
 * from CSR
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS sme_qos_process_reassoc_success_ev(struct mac_context *mac_ctx,
				uint8_t sessionid, void *event_info)
{

	struct csr_roam_session *csr_roam_session = NULL;
	struct sme_qos_sessioninfo *qos_session;
	struct sme_qos_acinfo *ac_info;
	enum qca_wlan_ac_type ac;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;

	sme_debug("invoked on session %d", sessionid);

	if (sessionid >= WLAN_MAX_VDEVS) {
		sme_err("invoked on session %d", sessionid);
		return status;
	}

	csr_roam_session = CSR_GET_SESSION(mac_ctx, sessionid);

	qos_session = &sme_qos_cb.sessionInfo[sessionid];

	/* get the association info */
	if (!event_info) {
		sme_err("event_info is NULL");
		return status;
	}

	if (!((sme_QosAssocInfo *)event_info)->bss_desc) {
		sme_err("bss_desc is NULL");
		return status;
	}
	status = sme_qos_save_assoc_info(qos_session, event_info);
	if (status)
		sme_err("sme_qos_save_assoc_info() failed");

	/*
	 * Assuming both handoff algo & 11r willn't be enabled
	 * at the same time
	 */
	if (qos_session->handoffRequested) {
		qos_session->handoffRequested = false;
		/* renew all flows */
		(void)sme_qos_process_buffered_cmd(sessionid);
		return QDF_STATUS_SUCCESS;
	}
	if (qos_session->ftHandoffInProgress) {
		if (csr_roam_is11r_assoc(mac_ctx, sessionid)) {
			if (csr_roam_session &&
			    csr_roam_session->connectedInfo.nRICRspLength) {
				status = sme_qos_process_ft_reassoc_rsp_ev(
						mac_ctx, sessionid,
						event_info);
			} else {
				sme_debug("session or RIC data is not present");
			}
		}
#ifdef FEATURE_WLAN_ESE
		/*
		 * If ESE association check for TSPEC IEs in the
		 * reassoc rsp frame
		 */
		if (csr_roam_is_ese_assoc(mac_ctx, sessionid)) {
			if (csr_roam_session &&
			    csr_roam_session->connectedInfo.nTspecIeLength) {
				status = sme_qos_ese_process_reassoc_tspec_rsp(
						mac_ctx, sessionid, event_info);
			}
		}
#endif
		qos_session->ftHandoffInProgress = false;
		qos_session->handoffRequested = false;
		return status;
	}

	qos_session->sessionActive = true;
	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
		ac_info = &qos_session->ac_info[ac];
		switch (ac_info->curr_state) {
		case SME_QOS_HANDOFF:
			status = sme_qos_handle_handoff_state(mac_ctx,
					qos_session, ac_info, ac, sessionid);
			break;
		case SME_QOS_INIT:
		case SME_QOS_CLOSED:
			/* NOP */
			status = QDF_STATUS_SUCCESS;
			break;
		case SME_QOS_LINK_UP:
		case SME_QOS_REQUESTED:
		case SME_QOS_QOS_ON:
		default:
			sme_err("session %d AC %d is in wrong state %d",
				sessionid, ac, ac_info->curr_state);
			break;
		}
	}
	(void)sme_qos_process_buffered_cmd(sessionid);
	return status;
}

/*
 * sme_qos_process_reassoc_failure_ev() - Function to process the
 *  SME_QOS_CSR_REASSOC_FAILURE event indication from CSR
 *
 * pEvent_info - Pointer to relevant info from CSR.
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_process_reassoc_failure_ev(struct mac_context *mac,
					   uint8_t sessionId, void *pEvent_info)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	enum qca_wlan_ac_type ac;

	sme_debug("invoked on session %d", sessionId);
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
		pACInfo = &pSession->ac_info[ac];
		switch (pACInfo->curr_state) {
		case SME_QOS_HANDOFF:
			sme_qos_state_transition(sessionId, ac, SME_QOS_INIT);
			if (pACInfo->reassoc_pending)
				pACInfo->reassoc_pending = false;

			qdf_mem_zero(&pACInfo->
				     curr_QoSInfo[SME_QOS_TSPEC_INDEX_0],
				     sizeof(struct sme_qos_wmmtspecinfo));
			qdf_mem_zero(&pACInfo->
				     requested_QoSInfo[SME_QOS_TSPEC_INDEX_0],
				     sizeof(struct sme_qos_wmmtspecinfo));
			qdf_mem_zero(&pACInfo->
				     curr_QoSInfo[SME_QOS_TSPEC_INDEX_1],
				     sizeof(struct sme_qos_wmmtspecinfo));
			qdf_mem_zero(&pACInfo->
				     requested_QoSInfo[SME_QOS_TSPEC_INDEX_1],
				     sizeof(struct sme_qos_wmmtspecinfo));
			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:
			sme_err("On session %d AC %d is in wrong state %d",
				sessionId, ac, pACInfo->curr_state);
			break;
		}
	}
	/* need to clean up flows */
	sme_qos_delete_existing_flows(mac, sessionId);
	return QDF_STATUS_SUCCESS;
}

#ifdef WLAN_FEATURE_ROAM_OFFLOAD
#ifdef FEATURE_WLAN_ESE
static bool sme_qos_ft_handoff_required(struct mac_context *mac,
					uint8_t session_id)
{
	struct csr_roam_session *csr_roam_session;

	if (csr_roam_is11r_assoc(mac, session_id))
		return true;

	csr_roam_session = CSR_GET_SESSION(mac, session_id);

	if (csr_roam_session &&
	    wlan_cm_is_roam_sync_in_progress(mac->psoc, session_id) &&
	    csr_roam_is_ese_assoc(mac, session_id) &&
	    csr_roam_session->connectedInfo.nTspecIeLength)
		return true;

	return false;
}
#else
static inline bool sme_qos_ft_handoff_required(struct mac_context *mac,
					       uint8_t session_id)
{
	return csr_roam_is11r_assoc(mac, session_id) ? true : false;
}
#endif
#else
static inline bool sme_qos_ft_handoff_required(struct mac_context *mac,
					       uint8_t session_id)
{
	return false;
}
#endif

#ifdef FEATURE_WLAN_ESE
static inline bool sme_qos_legacy_handoff_required(struct mac_context *mac,
						   uint8_t session_id)
{
	return csr_roam_is_ese_assoc(mac, session_id) ? false : true;
}
#else
static inline bool sme_qos_legacy_handoff_required(struct mac_context *mac,
						   uint8_t session_id)
{
	return true;
}
#endif

/*
 * sme_qos_process_handoff_assoc_req_ev() - Function to process the
 *  SME_QOS_CSR_HANDOFF_ASSOC_REQ event indication from CSR
 *
 * pEvent_info - Pointer to relevant info from CSR.
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_process_handoff_assoc_req_ev(struct mac_context *mac,
					uint8_t sessionId, void *pEvent_info)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	uint8_t ac;

	sme_debug("invoked on session %d", sessionId);
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; 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_qos_state_transition(sessionId, ac,
						SME_QOS_HANDOFF);
			break;
		case SME_QOS_HANDOFF:
			/* print error msg */
			if (pSession->ftHandoffInProgress) {
				sme_debug("SME_QOS_CSR_HANDOFF_ASSOC_REQ received in SME_QOS_HANDOFF state with FT in progress");
				break;
			}
			fallthrough;
		case SME_QOS_CLOSED:
		case SME_QOS_INIT:
		default:
			sme_err("On session %d AC %d is in wrong state %d",
				sessionId, ac, pACInfo->curr_state);
			break;
		}
	}

	if (sme_qos_ft_handoff_required(mac, sessionId))
		pSession->ftHandoffInProgress = true;

	/* If FT handoff/ESE in progress, legacy handoff need not be enabled */
	if (!pSession->ftHandoffInProgress &&
	    sme_qos_legacy_handoff_required(mac, sessionId))
		pSession->handoffRequested = true;

	/* this session no longer needs UAPSD */
	pSession->apsdMask = 0;
	/* do any sessions still require UAPSD? */
	sme_ps_uapsd_disable(MAC_HANDLE(mac), sessionId);
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_process_handoff_success_ev() - Function to process the
 *  SME_QOS_CSR_HANDOFF_COMPLETE event indication from CSR
 *
 * pEvent_info - Pointer to relevant info from CSR.
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_process_handoff_success_ev(struct mac_context *mac,
					   uint8_t sessionId, void *pEvent_info)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	uint8_t ac;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;

	sme_debug("invoked on session %d", sessionId);
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	/* go back to original state before handoff */
	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
		pACInfo = &pSession->ac_info[ac];
		switch (pACInfo->curr_state) {
		case SME_QOS_HANDOFF:
			sme_qos_state_transition(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 = QDF_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:
/* 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;
			sme_err("On session %d AC %d is in wrong state %d",
				sessionId, ac, pACInfo->curr_state);
			break;
		}
	}
	return status;
}

/*
 * sme_qos_process_disconnect_ev() - Function to process the
 *  SME_QOS_CSR_DISCONNECT_REQ or  SME_QOS_CSR_DISCONNECT_IND event indication
 *  from CSR
 *
 * pEvent_info - Pointer to relevant info from CSR.
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_process_disconnect_ev(struct mac_context *mac, uint8_t
					sessionId, void *pEvent_info)
{
	struct sme_qos_sessioninfo *pSession;

	sme_debug("invoked on session %d", sessionId);
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	/*
	 * 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->handoffRequested)
	    && !pSession->ftHandoffInProgress) {
		sme_debug("no need for state transition, should already be in handoff state");
		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)) {
			sme_err("curr_state is not HANDOFF, session %d",
				sessionId);
			return QDF_STATUS_SUCCESS;
		}

		return QDF_STATUS_SUCCESS;
	}
	sme_qos_init_a_cs(mac, sessionId);
	/* this session doesn't require UAPSD */
	pSession->apsdMask = 0;

	sme_ps_uapsd_disable(MAC_HANDLE(mac), sessionId);

	pSession->handoffRequested = false;
	pSession->roamID = 0;
	/* need to clean up buffered req */
	sme_qos_delete_buffered_requests(mac, sessionId);
	/* need to clean up flows */
	sme_qos_delete_existing_flows(mac, sessionId);
	/* clean up the assoc info */
	if (pSession->assocInfo.bss_desc) {
		qdf_mem_free(pSession->assocInfo.bss_desc);
		pSession->assocInfo.bss_desc = NULL;
	}
	sme_qos_cb.sessionInfo[sessionId].sessionActive = false;
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_process_join_req_ev() - Function to process the
 *  SME_QOS_CSR_JOIN_REQ event indication from CSR
 *
 * pEvent_info - Pointer to relevant info from CSR.
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_process_join_req_ev(struct mac_context *mac, uint8_t
						sessionId, void *pEvent_info)
{
	struct sme_qos_sessioninfo *pSession;
	enum qca_wlan_ac_type ac;

	pSession = &sme_qos_cb.sessionInfo[sessionId];
	if (pSession->handoffRequested) {
		sme_debug("No need for state transition, should already be in handoff state");
		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))
			sme_err("curr_state is not HANDOFF, session %d",
				sessionId);
		/* buffer the existing flows to be renewed after handoff is
		 * done
		 */
		sme_qos_buffer_existing_flows(mac, sessionId);
		/* clean up the control block partially for handoff */
		sme_qos_cleanup_ctrl_blk_for_handoff(mac, sessionId);
		return QDF_STATUS_SUCCESS;
	}

	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++)
		sme_qos_state_transition(sessionId, ac, SME_QOS_INIT);

	/* clean up the assoc info if already set */
	if (pSession->assocInfo.bss_desc) {
		qdf_mem_free(pSession->assocInfo.bss_desc);
		pSession->assocInfo.bss_desc = NULL;
	}
	return QDF_STATUS_SUCCESS;
}

#ifdef WLAN_FEATURE_HOST_ROAM
/**
 * sme_qos_process_preauth_success_ind() - process preauth success indication
 * @mac_ctx: global MAC context
 * @sessionid: session ID
 * @event_info: event buffer
 *
 * Function to process the SME_QOS_CSR_PREAUTH_SUCCESS_IND event indication
 * from CSR
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS sme_qos_process_preauth_success_ind(struct mac_context *mac_ctx,
				uint8_t sessionid, void *event_info)
{
	struct sme_qos_sessioninfo *qos_session;
	struct csr_roam_session *sme_session = CSR_GET_SESSION(mac_ctx,
							       sessionid);
	struct sme_qos_acinfo *ac_info;
	uint8_t ac;
	QDF_STATUS status = QDF_STATUS_SUCCESS;
	uint16_t ric_offset = 0;
	uint32_t ric_ielen = 0;
	uint8_t *ric_ie;
	uint8_t tspec_mask_status = 0;
	uint8_t tspec_pending_status = 0;
	struct wlan_objmgr_vdev *vdev;
	struct mlme_legacy_priv *mlme_priv;

	sme_debug("invoked on SME session %d", sessionid);

	if (!sme_session) {
		sme_err("sme_session is NULL");
		return QDF_STATUS_E_INVAL;
	}

	qos_session = &sme_qos_cb.sessionInfo[sessionid];

	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
		ac_info = &qos_session->ac_info[ac];

		switch (ac_info->curr_state) {
		case SME_QOS_LINK_UP:
		case SME_QOS_REQUESTED:
		case SME_QOS_QOS_ON:
		    sme_qos_state_transition(sessionid, ac, SME_QOS_HANDOFF);
			break;
		case SME_QOS_HANDOFF:
		/* print error msg */
		case SME_QOS_CLOSED:
		case SME_QOS_INIT:
		default:
			sme_err("Session %d AC %d is in wrong state %d",
				sessionid, ac, ac_info->curr_state);
			break;
		}
	}

	qos_session->ftHandoffInProgress = true;

	/* Check if its a 11R roaming before preparing the RIC IEs */
	if (!csr_roam_is11r_assoc(mac_ctx, sessionid))
		return status;

	vdev = wlan_objmgr_get_vdev_by_id_from_pdev(mac_ctx->pdev, sessionid,
						    WLAN_LEGACY_SME_ID);
	if (!vdev)
		return QDF_STATUS_E_INVAL;
	mlme_priv = wlan_vdev_mlme_get_ext_hdl(vdev);
	if (!mlme_priv) {
		status = QDF_STATUS_E_FAILURE;
		goto end;
	}

	/*
	 * 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
	 */
	qdf_mem_zero(mlme_priv->connect_info.ft_info.ric_ies, MAX_FTIE_SIZE);
	mlme_priv->connect_info.ft_info.ric_ies_length = 0;

	ric_ie = mlme_priv->connect_info.ft_info.ric_ies;
	ric_offset = mlme_priv->connect_info.ft_info.ric_ies_length;

	/*
	 * Now we have to process the currentTspeInfo inside this session and
	 * create the RIC IEs
	 */
	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
		volatile uint8_t tspec_idx = 0;

		ric_ielen = 0;
		ac_info = &qos_session->ac_info[ac];
		tspec_pending_status = ac_info->tspec_pending;
		tspec_mask_status = ac_info->tspec_mask_status;
		qdf_mem_zero(ac_info->ricIdentifier, SME_QOS_TSPEC_INDEX_MAX);
		sme_debug("AC %d ==> TSPEC status = %d, tspec pending = %d",
			  ac, tspec_mask_status, tspec_pending_status);

		do {
			if (!(tspec_mask_status & 0x1))
				goto add_next_ric;

			/*
			 * 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_qos_create_tspec_ricie(mac_ctx,
					&ac_info->requested_QoSInfo[tspec_idx],
					ric_ie + ric_offset, &ric_ielen,
					&ac_info->ricIdentifier[tspec_idx]);
			} else {
				status = sme_qos_create_tspec_ricie(mac_ctx,
					&ac_info->curr_QoSInfo[tspec_idx],
					ric_ie + ric_offset, &ric_ielen,
					&ac_info->ricIdentifier[tspec_idx]);
			}
add_next_ric:
			ric_offset += ric_ielen;
			mlme_priv->connect_info.ft_info.ric_ies_length = ric_ielen;
			tspec_mask_status >>= 1;
			tspec_pending_status >>= 1;
			tspec_idx++;
		} while (tspec_mask_status);
	}
end:
	wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_SME_ID);

	return status;
}
#else
static inline
QDF_STATUS sme_qos_process_preauth_success_ind(struct mac_context *mac_ctx,
				uint8_t sessionid, void *event_info)
{
	return QDF_STATUS_SUCCESS;
}
#endif
/*
 * sme_qos_process_add_ts_failure_rsp() - 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
 *
 * mac - Pointer to the global MAC parameter structure.
 * pRsp - Pointer to the addts response structure came from PE.
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_process_add_ts_failure_rsp(struct mac_context *mac,
					      uint8_t sessionId,
					      tSirAddtsRspInfo *pRsp)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	enum qca_wlan_ac_type ac;
	struct sme_qos_searchinfo search_key;
	uint8_t tspec_pending;
	enum sme_qos_wmmuptype up =
		(enum sme_qos_wmmuptype) pRsp->tspec.tsinfo.traffic.userPrio;

	sme_debug("invoked on session %d for UP %d", sessionId, up);
	ac = sme_qos_up_to_ac(up);
	if (QCA_WLAN_AC_ALL == ac) {
		/* err msg */
		sme_err("invalid AC %d from UP %d", ac, up);
		return QDF_STATUS_E_FAILURE;
	}
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	pACInfo = &pSession->ac_info[ac];
	/* is there a TSPEC request pending on this AC? */
	tspec_pending = pACInfo->tspec_pending;
	if (!tspec_pending) {
		sme_err("On session %d an AddTS is not pending on AC %d",
			sessionId, ac);
		return QDF_STATUS_E_FAILURE;
	}

	qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo));
	/* 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 (!QDF_IS_STATUS_SUCCESS
		    (sme_qos_find_all_in_flow_list
			    (mac, search_key, sme_qos_add_ts_failure_fnp))) {
		sme_err("On session %d no match found for ac = %d",
			sessionId, search_key.key.ac_type);
		return QDF_STATUS_E_FAILURE;
	}
	qdf_mem_zero(&pACInfo->requested_QoSInfo[tspec_pending - 1],
		     sizeof(struct sme_qos_wmmtspecinfo));

	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_qos_state_transition(sessionId, ac, SME_QOS_LINK_UP);
	} else
		sme_qos_state_transition(sessionId, ac, SME_QOS_QOS_ON);

	pACInfo->tspec_pending = 0;

	(void)sme_qos_process_buffered_cmd(sessionId);

	return QDF_STATUS_SUCCESS;
}

/**
 * sme_qos_update_tspec_mask() - Utility function to update the tspec.
 * @sessionid: Session upon which the TSPEC is being updated
 * @search_key: search key
 * @new_tspec_mask: tspec to be set for this AC
 *
 * Typical usage while aggregating unidirectional flows into a bi-directional
 * flow on AC which is running multiple flows
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS sme_qos_update_tspec_mask(uint8_t sessionid,
					    struct sme_qos_searchinfo
						search_key,
					    uint8_t new_tspec_mask)
{
	tListElem *list_elt = NULL, *list_next_elt = NULL;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	struct sme_qos_sessioninfo *qos_session;
	struct sme_qos_acinfo *ac_info;

	sme_debug("invoked on session %d for AC %d TSPEC %d",
		  sessionid, search_key.key.ac_type, new_tspec_mask);

	qos_session = &sme_qos_cb.sessionInfo[sessionid];

	if (search_key.key.ac_type < QCA_WLAN_AC_ALL) {
		ac_info = &qos_session->ac_info[search_key.key.ac_type];
	} else {
		sme_debug("Exceeded the array bounds");
		return QDF_STATUS_E_FAILURE;
	}

	list_elt = csr_ll_peek_head(&sme_qos_cb.flow_list, false);
	if (!list_elt) {
		sme_err("Flow List empty, nothing to update");
		return QDF_STATUS_E_FAILURE;
	}

	while (list_elt) {
		list_next_elt = csr_ll_next(&sme_qos_cb.flow_list, list_elt,
					    false);
		flow_info = GET_BASE_ADDR(list_elt, struct
					sme_qos_flowinfoentry, link);

		if (search_key.sessionId != flow_info->sessionId) {
			list_elt = list_next_elt;
			continue;
		}

		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)) {
				sme_debug("Flow %d matches",
					  flow_info->QosFlowID);
				ac_info->num_flows[flow_info->tspec_mask - 1]--;
				ac_info->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)) {
				sme_debug("Flow %d matches",
					  flow_info->QosFlowID);
				ac_info->num_flows[flow_info->tspec_mask - 1]--;
				ac_info->num_flows[new_tspec_mask - 1]++;
				flow_info->tspec_mask = new_tspec_mask;
			}
		}
		list_elt = list_next_elt;
	}

	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_process_add_ts_success_rsp() - 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
 *
 * mac - Pointer to the global MAC parameter structure.
 * pRsp - Pointer to the addts response structure came from PE.
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_process_add_ts_success_rsp(struct mac_context *mac,
					      uint8_t sessionId,
					      tSirAddtsRspInfo *pRsp)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	enum qca_wlan_ac_type ac, ac_index;
	struct sme_qos_searchinfo search_key;
	struct sme_qos_searchinfo search_key1;
	uint8_t tspec_pending;
	tListElem *pEntry = NULL;
	enum QDF_OPMODE opmode;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	enum sme_qos_wmmuptype up =
		(enum sme_qos_wmmuptype) pRsp->tspec.tsinfo.traffic.userPrio;
#ifdef FEATURE_WLAN_DIAG_SUPPORT
	WLAN_HOST_DIAG_EVENT_DEF(qos, host_event_wlan_qos_payload_type);
	host_log_qos_tspec_pkt_type *log_ptr = NULL;
#endif /* FEATURE_WLAN_DIAG_SUPPORT */

	sme_debug("invoked on session %d for UP %d", sessionId, up);
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	ac = sme_qos_up_to_ac(up);
	if (QCA_WLAN_AC_ALL == ac) {
		/* err msg */
		sme_err("invalid AC %d from UP %d", ac, up);
		return QDF_STATUS_E_FAILURE;
	}
	pACInfo = &pSession->ac_info[ac];
	/* is there a TSPEC request pending on this AC? */
	tspec_pending = pACInfo->tspec_pending;
	if (!tspec_pending) {
		sme_err("On session %d an AddTS is not pending on AC %d",
			sessionId, ac);
		return QDF_STATUS_E_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 << (QCA_WLAN_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 << (QCA_WLAN_AC_VO - ac));
		} else {
			sme_debug("Exceeded the array bounds of pACInfo->requested_QosInfo");
			return QDF_STATUS_E_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;

	sme_set_tspec_uapsd_mask_per_session(mac,
			&pRsp->tspec.tsinfo, sessionId);

	sme_debug("On session %d AddTspec Medium Time %d",
		  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)) {
		qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo));
		/* 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_qos_update_tspec_mask(sessionId, search_key,
					  SME_QOS_TSPEC_MASK_BIT_1_SET);
	}

	qdf_mem_zero(&search_key1, sizeof(struct sme_qos_searchinfo));
	/* 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 = QCA_WLAN_AC_BE; ac_index < QCA_WLAN_AC_ALL;
	     ac_index++) {
		pEntry = sme_qos_find_in_flow_list(search_key1);
		if (pEntry) {
			flow_info = GET_BASE_ADDR(pEntry,
					struct sme_qos_flowinfoentry, link);
			if (flow_info->ac_type == ac) {
				pACInfo->hoRenewal = flow_info->hoRenewal;
				break;
			}
		}
	}
	qdf_mem_zero(&search_key, sizeof(struct sme_qos_searchinfo));
	/* 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 (!QDF_IS_STATUS_SUCCESS
		    (sme_qos_find_all_in_flow_list
			    (mac, search_key, sme_qos_add_ts_success_fnp))) {
		sme_err("On session %d no match found for ac %d", sessionId,
			search_key.key.ac_type);
		return QDF_STATUS_E_FAILURE;
	}
	pACInfo->hoRenewal = false;
	qdf_mem_zero(&pACInfo->requested_QoSInfo[tspec_pending - 1],
		     sizeof(struct sme_qos_wmmtspecinfo));
	/* 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_HOST_DIAG_EVENT_REPORT(&qos, EVENT_WLAN_QOS);
	WLAN_HOST_DIAG_LOG_ALLOC(log_ptr, host_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_HOST_DIAG_LOG_REPORT(log_ptr);
#endif /* FEATURE_WLAN_DIAG_SUPPORT */
	pACInfo->tspec_pending = 0;

	sme_qos_state_transition(sessionId, ac, SME_QOS_QOS_ON);

	/* Inform this TSPEC IE change to FW */
	opmode = wlan_get_opmode_from_vdev_id(mac->pdev, sessionId);
	if (opmode == QDF_STA_MODE)
		wlan_roam_update_cfg(mac->psoc, sessionId,
				    REASON_CONNECT_IES_CHANGED);

	(void)sme_qos_process_buffered_cmd(sessionId);
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_aggregate_params() - Utility function to increment the TSPEC
 *  params per AC. Typical usage while using flow aggregation or deletion of
 *  flows
 *
 * pInput_Tspec_Info - Pointer to sme_QosWmmTspecInfo which contains the
 *  WMM TSPEC related info with which pCurrent_Tspec_Info will be updated
 * pCurrent_Tspec_Info - Pointer to sme_QosWmmTspecInfo which contains
 *  current the WMM TSPEC related info
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_aggregate_params(
		struct sme_qos_wmmtspecinfo *pInput_Tspec_Info,
		struct sme_qos_wmmtspecinfo *pCurrent_Tspec_Info,
		struct sme_qos_wmmtspecinfo *pUpdated_Tspec_Info)
{
	struct sme_qos_wmmtspecinfo TspecInfo;

	sme_debug("invoked");
	if (!pInput_Tspec_Info) {
		sme_err("input is NULL, nothing to aggregate");
		return QDF_STATUS_E_FAILURE;
	}
	if (!pCurrent_Tspec_Info) {
		sme_err("Current is NULL, can't aggregate");
		return QDF_STATUS_E_FAILURE;
	}
	qdf_mem_copy(&TspecInfo, pCurrent_Tspec_Info,
		     sizeof(struct sme_qos_wmmtspecinfo));
	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 =
			QDF_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 =
			QDF_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 =
		QDF_MAX(pCurrent_Tspec_Info->maximum_msdu_size,
			pInput_Tspec_Info->maximum_msdu_size);

	/* Inactivity interval : these sizes are `maxed' */
	TspecInfo.inactivity_interval =
		QDF_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 =
			QDF_MIN(pCurrent_Tspec_Info->delay_bound,
				pInput_Tspec_Info->delay_bound);
	} else
		TspecInfo.delay_bound = pInput_Tspec_Info->delay_bound;

	TspecInfo.max_burst_size = QDF_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 =
		QDF_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)
		qdf_mem_copy(pUpdated_Tspec_Info, &TspecInfo,
			     sizeof(struct sme_qos_wmmtspecinfo));
	else
		qdf_mem_copy(pCurrent_Tspec_Info, &TspecInfo,
			     sizeof(struct sme_qos_wmmtspecinfo));

	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_update_params() - Utility function to update the TSPEC
 *  params per AC. Typical usage while deleting flows on AC which is running
 *  multiple flows
 *
 * sessionId - Session upon which the TSPEC is being updated
 * ac - Enumeration of the various EDCA Access Categories.
 * tspec_mask - on which tspec per AC, the update is requested
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_update_params(uint8_t sessionId,
		enum qca_wlan_ac_type ac,
		uint8_t tspec_mask,
		struct sme_qos_wmmtspecinfo *pTspec_Info)
{
	tListElem *pEntry = NULL, *pNextEntry = NULL;
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	struct sme_qos_wmmtspecinfo Tspec_Info;

	sme_debug("invoked on session %d for AC %d TSPEC %d",
		  sessionId, ac, tspec_mask);
	if (!pTspec_Info) {
		sme_err("output is NULL, can't aggregate");
		return QDF_STATUS_E_FAILURE;
	}
	qdf_mem_zero(&Tspec_Info, sizeof(struct sme_qos_wmmtspecinfo));
	pEntry = csr_ll_peek_head(&sme_qos_cb.flow_list, false);
	if (!pEntry) {
		sme_err("Flow List empty, nothing to update");
		return QDF_STATUS_E_FAILURE;
	}
	pSession = &sme_qos_cb.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 = csr_ll_next(&sme_qos_cb.flow_list, pEntry, false);
		flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry,
					link);
		if ((sessionId == flow_info->sessionId) &&
		    (ac == flow_info->ac_type) &&
		    (tspec_mask == flow_info->tspec_mask)) {
			sme_debug("Flow %d matches", flow_info->QosFlowID);

			if ((SME_QOS_REASON_RELEASE == flow_info->reason) ||
			    (SME_QOS_REASON_MODIFY == flow_info->reason)) {
				/* msg */
				sme_debug("Skipping Flow %d as it is marked for release/modify",
					  flow_info->QosFlowID);
			} else
			if (!QDF_IS_STATUS_SUCCESS
				    (sme_qos_aggregate_params
					    (&flow_info->QoSInfo, &Tspec_Info,
						NULL))) {
				/* err msg */
				sme_err("sme_qos_aggregate_params() failed");
			}
		}
		pEntry = pNextEntry;
	}
	/* return the aggregate */
	*pTspec_Info = Tspec_Info;
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_up_to_ac() - Utility function to map an UP to AC
 *
 * up - Enumeration of the various User priorities (UP).
 * Return an Access Category
 */
static enum qca_wlan_ac_type sme_qos_up_to_ac(enum sme_qos_wmmuptype up)
{
	enum qca_wlan_ac_type ac = QCA_WLAN_AC_ALL;

	if (up >= 0 && up < SME_QOS_WMM_UP_MAX)
		ac = sme_qos_up_to_ac_map[up];

	sme_debug("up = %d ac = %d returned", up, ac);
	return ac;
}

/*
 * sme_qos_state_transition() - The state transition function per AC. We
 *  save the previous state also.
 *
 * sessionId - Session upon which the state machine is running
 * ac - Enumeration of the various EDCA Access Categories.
 * new_state - The state FSM is moving to.
 *
 * Return None
 */
static void sme_qos_state_transition(uint8_t sessionId,
				     enum qca_wlan_ac_type ac,
				     enum sme_qos_states new_state)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;

	pSession = &sme_qos_cb.sessionInfo[sessionId];
	pACInfo = &pSession->ac_info[ac];
	pACInfo->prev_state = pACInfo->curr_state;
	pACInfo->curr_state = new_state;
	if (pACInfo->curr_state != pACInfo->prev_state)
		sme_debug("On session %d new %d old %d, for AC %d",
			  sessionId, pACInfo->curr_state,
			  pACInfo->prev_state, ac);
}

/**
 * sme_qos_find_in_flow_list() - find a flow entry from the flow list
 * @search_key: We can either use the flowID or the ac type to find the
 *              entry in the flow list.
 *              A bitmap in struct sme_qos_searchinfo tells which key to use.
 *              Starting from LSB,
 *              bit 0 - Flow ID
 *              bit 1 - AC type
 *
 * Utility function to find an flow entry from the flow_list.
 *
 * Return: pointer to the list element
 */
static tListElem *sme_qos_find_in_flow_list(struct sme_qos_searchinfo
						search_key)
{
	tListElem *list_elt = NULL, *list_next_elt = NULL;
	struct sme_qos_flowinfoentry *flow_info = NULL;

	list_elt = csr_ll_peek_head(&sme_qos_cb.flow_list, false);
	if (!list_elt) {
		sme_err("Flow List empty, can't search");
		return NULL;
	}

	while (list_elt) {
		list_next_elt = csr_ll_next(&sme_qos_cb.flow_list, list_elt,
					    false);
		flow_info = GET_BASE_ADDR(list_elt, struct
					sme_qos_flowinfoentry, link);

		if ((search_key.sessionId != flow_info->sessionId) &&
		    (search_key.sessionId != SME_QOS_SEARCH_SESSION_ID_ANY)) {
			list_elt = list_next_elt;
			continue;
		}

		if (search_key.index & SME_QOS_SEARCH_KEY_INDEX_1) {
			if (search_key.key.QosFlowID == flow_info->QosFlowID) {
				sme_debug("match found on flowID, ending search");
				break;
			}
		} else if (search_key.index & SME_QOS_SEARCH_KEY_INDEX_2) {
			if (search_key.key.ac_type == flow_info->ac_type) {
				sme_debug("match found on ac, ending search");
				break;
			}
		} else if (search_key.index & SME_QOS_SEARCH_KEY_INDEX_3) {
			if (search_key.key.reason == flow_info->reason) {
				sme_debug("match found on reason, ending search");
				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)) {
				sme_debug("match found on reason, ending search");
				break;
			}
		}
		list_elt = list_next_elt;
	}
	return list_elt;
}

/**
 * sme_qos_find_all_in_flow_list() - find a flow entry in the flow list
 * @mac_ctx: global MAC context
 * @search_key: search key
 * @fnp: function pointer specifying the action type for the entry found
 *
 * Utility function to find an flow entry from the flow_list & act on it.
 * search_key -  We can either use the flowID or the ac type to find the
 *   entry in the flow list.
 *  A bitmap in struct sme_qos_searchinfo tells which key to use. Starting from
 * LSB,
 *  bit 0 - Flow ID
 *  bit 1 - AC type
 *
 * Return: None
 */
static QDF_STATUS sme_qos_find_all_in_flow_list(struct mac_context *mac_ctx,
					 struct sme_qos_searchinfo search_key,
					 sme_QosProcessSearchEntry fnp)
{
	tListElem *list_elt = NULL, *list_next_elt = NULL;
	struct sme_qos_sessioninfo *qos_session;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;
	enum qca_wlan_ac_type ac_type;

	list_elt = csr_ll_peek_head(&sme_qos_cb.flow_list, false);
	if (!list_elt) {
		sme_err("Flow List empty, can't search");
		return QDF_STATUS_E_FAILURE;
	}

	while (list_elt) {
		list_next_elt = csr_ll_next(&sme_qos_cb.flow_list, list_elt,
					    false);
		flow_info = GET_BASE_ADDR(list_elt, struct
					sme_qos_flowinfoentry, link);
		qos_session = &sme_qos_cb.sessionInfo[flow_info->sessionId];
		if ((search_key.sessionId != flow_info->sessionId) &&
		    (search_key.sessionId != SME_QOS_SEARCH_SESSION_ID_ANY)) {
			list_elt = list_next_elt;
			continue;
		}

		if ((search_key.index & SME_QOS_SEARCH_KEY_INDEX_1) &&
		    (search_key.key.QosFlowID == flow_info->QosFlowID)) {
			sme_debug("match found on flowID, ending search");
			status = fnp(mac_ctx, list_elt);
			if (QDF_STATUS_E_FAILURE == status) {
				sme_err("Failed to process entry");
				break;
			}
		} else if ((search_key.index & SME_QOS_SEARCH_KEY_INDEX_2) &&
			   (search_key.key.ac_type == flow_info->ac_type)) {
			sme_debug("match found on ac, ending search");
			ac_type = flow_info->ac_type;
			flow_info->hoRenewal =
				qos_session->ac_info[ac_type].hoRenewal;
			status = fnp(mac_ctx, list_elt);
			if (QDF_STATUS_E_FAILURE == status) {
				sme_err("Failed to process entry");
				break;
			}
		}
		list_elt = list_next_elt;
	}
	return status;
}

/*
 * sme_qos_is_acm() - Utility function to check if a particular AC
 *  mandates Admission Control.
 *
 * ac - Enumeration of the various EDCA Access Categories.
 *
 * Return true if the AC mandates Admission Control
 */
static bool
sme_qos_is_acm(struct mac_context *mac, struct bss_description *pSirBssDesc,
	       enum qca_wlan_ac_type ac, tDot11fBeaconIEs *pIes)
{
	bool ret_val = false;
	tDot11fBeaconIEs *pIesLocal;

	if (pIes)
		/* IEs were provided so use them locally */
		pIesLocal = pIes;
	else {
		/* IEs were not provided so parse them ourselves */
		if (!QDF_IS_STATUS_SUCCESS
			    (csr_get_parsed_bss_description_ies
				    (mac, pSirBssDesc, &pIesLocal))) {
			/* err msg */
			sme_err("csr_get_parsed_bss_description_ies() failed");
			return false;
		}

		/* if success then pIesLocal was allocated */
	}

	if (CSR_IS_QOS_BSS(pIesLocal)) {
		switch (ac) {
		case QCA_WLAN_AC_BE:
			if (pIesLocal->WMMParams.acbe_acm)
				ret_val = true;
			break;
		case QCA_WLAN_AC_BK:
			if (pIesLocal->WMMParams.acbk_acm)
				ret_val = true;
			break;
		case QCA_WLAN_AC_VI:
			if (pIesLocal->WMMParams.acvi_acm)
				ret_val = true;
			break;
		case QCA_WLAN_AC_VO:
			if (pIesLocal->WMMParams.acvo_acm)
				ret_val = true;
			break;
		default:
			sme_err("unknown AC = %d", ac);
			break;
		}
	} /* IS_QOS_BSS */
	if (!pIes)
		/* IEs were allocated locally so free them */
		qdf_mem_free(pIesLocal);

	return ret_val;
}

/**
 * sme_qos_buffer_existing_flows() - buffer existing flows in flow_list
 * @mac_ctx: global MAC context
 * @sessionid: session ID
 *
 * Utility function to buffer the existing flows in flow_list,
 * so that we can renew them after handoff is done.
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS sme_qos_buffer_existing_flows(struct mac_context *mac_ctx,
						uint8_t sessionid)
{
	tListElem *list_entry = NULL, *list_nextentry = NULL;
	struct sme_qos_sessioninfo *qos_session;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	struct sme_qos_cmdinfo cmd;
	struct sme_qos_setupcmdinfo *setupinfo;

	list_entry = csr_ll_peek_head(&sme_qos_cb.flow_list, false);
	if (!list_entry) {
		sme_err("Flow List empty, nothing to buffer");
		return QDF_STATUS_E_FAILURE;
	}

	while (list_entry) {
		list_nextentry = csr_ll_next(&sme_qos_cb.flow_list, list_entry,
					     false);
		flow_info = GET_BASE_ADDR(list_entry, struct
					sme_qos_flowinfoentry, link);
		if (flow_info->sessionId != sessionid) {
			list_entry = list_nextentry;
			continue;
		}

		if ((SME_QOS_REASON_REQ_SUCCESS == flow_info->reason) ||
		    (SME_QOS_REASON_SETUP == flow_info->reason)) {
			cmd.command = SME_QOS_SETUP_REQ;
			cmd.mac = mac_ctx;
			cmd.sessionId = sessionid;
			setupinfo = &cmd.u.setupCmdInfo;

			setupinfo->HDDcontext = flow_info->HDDcontext;
			setupinfo->QoSInfo = flow_info->QoSInfo;
			setupinfo->QoSCallback = flow_info->QoSCallback;
			/* shouldn't be needed */
			setupinfo->UPType = SME_QOS_WMM_UP_MAX;
			setupinfo->QosFlowID = flow_info->QosFlowID;
			if (SME_QOS_REASON_SETUP == flow_info->reason)
				setupinfo->hoRenewal = false;
			else
				setupinfo->hoRenewal = true;

			if (!QDF_IS_STATUS_SUCCESS
				    (sme_qos_buffer_cmd(&cmd, true)))
				sme_err("couldn't buffer the setup request for flow %d in handoff state",
					flow_info->QosFlowID);
			else
				sme_debug("buffered a setup request for flow %d in handoff state",
					  flow_info->QosFlowID);
		} else if (SME_QOS_REASON_RELEASE == flow_info->reason) {
			cmd.command = SME_QOS_RELEASE_REQ;
			cmd.mac = mac_ctx;
			cmd.sessionId = sessionid;
			cmd.u.releaseCmdInfo.QosFlowID = flow_info->QosFlowID;
			if (!QDF_IS_STATUS_SUCCESS
				    (sme_qos_buffer_cmd(&cmd, true)))
				sme_err("couldn't buffer the release req for flow %d in handoff state",
					flow_info->QosFlowID);
			else
				sme_debug("buffered a release request for flow %d in handoff state",
					  flow_info->QosFlowID);
		} else if (SME_QOS_REASON_MODIFY_PENDING ==
			   flow_info->reason) {
			cmd.command = SME_QOS_MODIFY_REQ;
			cmd.mac = mac_ctx;
			cmd.sessionId = sessionid;
			cmd.u.modifyCmdInfo.QosFlowID = flow_info->QosFlowID;
			cmd.u.modifyCmdInfo.QoSInfo = flow_info->QoSInfo;
			if (!QDF_IS_STATUS_SUCCESS
				    (sme_qos_buffer_cmd(&cmd, true)))
				sme_err("couldn't buffer the modify req for flow %d in handoff state",
					flow_info->QosFlowID);
			else
				sme_debug("buffered a modify request for flow %d in handoff state",
					  flow_info->QosFlowID);
		}
		/* delete the entry from Flow List */
		sme_debug("Deleting original entry at %pK with flowID %d",
			  flow_info, flow_info->QosFlowID);
		csr_ll_remove_entry(&sme_qos_cb.flow_list, list_entry, true);
		qdf_mem_free(flow_info);

		list_entry = list_nextentry;
	}
	qos_session = &sme_qos_cb.sessionInfo[sessionid];
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_delete_existing_flows() - Utility function to Delete the existing
 * flows in flow_list, if we lost connectivity.
 *
 *  Return QDF_STATUS
 */
static QDF_STATUS sme_qos_delete_existing_flows(struct mac_context *mac,
						uint8_t sessionId)
{
	tListElem *pEntry = NULL, *pNextEntry = NULL;
	struct sme_qos_flowinfoentry *flow_info = NULL;

	pEntry = csr_ll_peek_head(&sme_qos_cb.flow_list, true);
	if (!pEntry) {
		sme_err("Flow List empty, nothing to delete");
		return QDF_STATUS_E_FAILURE;
	}
	while (pEntry) {
		pNextEntry = csr_ll_next(&sme_qos_cb.flow_list, pEntry, true);
		flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry,
					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(MAC_HANDLE(mac),
						       flow_info->HDDcontext,
						       NULL,
					SME_QOS_STATUS_RELEASE_QOS_LOST_IND,
						       flow_info->QosFlowID);
			}
			sme_debug("Deleting entry at %pK with flowID %d",
				  flow_info, flow_info->QosFlowID);
			/* delete the entry from Flow List */
			csr_ll_remove_entry(&sme_qos_cb.flow_list, pEntry,
					    true);
			qdf_mem_free(flow_info);
		}
		pEntry = pNextEntry;
	}
	return QDF_STATUS_SUCCESS;
}

/**
 * sme_qos_buffer_cmd() - buffer a request.
 * @pcmd: a pointer to the cmd structure to be saved inside the buffered
 *               cmd link list
 * @insert_head: flag indicate if cmd should be added to the list head.
 *
 * Utility function to buffer a request (setup/modify/release) from client
 * while processing another one on the same AC.
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS sme_qos_buffer_cmd(struct sme_qos_cmdinfo *pcmd,
					bool insert_head)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_cmdinfoentry *pentry = NULL;

	sme_debug("Invoked");
	pentry = qdf_mem_malloc(sizeof(struct sme_qos_cmdinfoentry));
	if (!pentry)
		return QDF_STATUS_E_NOMEM;

	/* copy the entire CmdInfo */
	pentry->cmdInfo = *pcmd;

	pSession = &sme_qos_cb.sessionInfo[pcmd->sessionId];
	if (insert_head)
		csr_ll_insert_head(&pSession->bufferedCommandList,
				&pentry->link, true);
	else
		csr_ll_insert_tail(&pSession->bufferedCommandList,
				&pentry->link, true);

	return QDF_STATUS_SUCCESS;
}

/**
 * sme_qos_process_buffered_cmd() - process qos buffered request
 * @session_id: Session ID
 *
 * Utility function to process a buffered request (setup/modify/release)
 * initially came from the client.
 *
 * Return:QDF_STATUS
 */
static QDF_STATUS sme_qos_process_buffered_cmd(uint8_t session_id)
{
	struct sme_qos_sessioninfo *qos_session;
	struct sme_qos_cmdinfoentry *pcmd = NULL;
	tListElem *list_elt = NULL;
	enum sme_qos_statustype hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
	QDF_STATUS qdf_ret_status = QDF_STATUS_SUCCESS;
	struct sme_qos_cmdinfo *qos_cmd = NULL;

	sme_debug("Invoked on session %d", session_id);
	qos_session = &sme_qos_cb.sessionInfo[session_id];
	if (!csr_ll_is_list_empty(&qos_session->bufferedCommandList, false)) {
		list_elt = csr_ll_remove_head(&qos_session->bufferedCommandList,
					      true);
		if (!list_elt) {
			sme_err("no more buffered commands on session %d",
				session_id);
			return QDF_STATUS_E_FAILURE;
		}
		pcmd = GET_BASE_ADDR(list_elt, struct sme_qos_cmdinfoentry,
					link);
		qos_cmd = &pcmd->cmdInfo;

		sme_debug("Qos cmd %d", qos_cmd->command);
		switch (qos_cmd->command) {
		case SME_QOS_SETUP_REQ:
			hdd_status = sme_qos_internal_setup_req(
				       qos_cmd->mac, qos_cmd->sessionId,
				       &qos_cmd->u.setupCmdInfo.QoSInfo,
				       qos_cmd->u.setupCmdInfo.QoSCallback,
				       qos_cmd->u.setupCmdInfo.HDDcontext,
				       qos_cmd->u.setupCmdInfo.UPType,
				       qos_cmd->u.setupCmdInfo.QosFlowID,
				       true, qos_cmd->u.setupCmdInfo.hoRenewal);
			if (SME_QOS_STATUS_SETUP_FAILURE_RSP == hdd_status) {
				sme_err("sme_qos_internal_setup_req failed on session %d",
					session_id);
				qdf_ret_status = QDF_STATUS_E_FAILURE;
			}
			break;
		case SME_QOS_RELEASE_REQ:
			hdd_status = sme_qos_internal_release_req(qos_cmd->mac,
					qos_cmd->sessionId,
					qos_cmd->u.releaseCmdInfo.QosFlowID,
					true);
			if (SME_QOS_STATUS_RELEASE_FAILURE_RSP == hdd_status) {
				sme_err("sme_qos_internal_release_req failed on session %d",
					session_id);
				qdf_ret_status = QDF_STATUS_E_FAILURE;
			}
			break;
		case SME_QOS_MODIFY_REQ:
			hdd_status = sme_qos_internal_modify_req(qos_cmd->mac,
					&qos_cmd->u.modifyCmdInfo.QoSInfo,
					qos_cmd->u.modifyCmdInfo.QosFlowID,
					true);
			if (SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP ==
				hdd_status) {
				sme_err("sme_qos_internal_modify_req failed on session %d",
					session_id);
				qdf_ret_status = QDF_STATUS_E_FAILURE;
			}
			break;
		case SME_QOS_RESEND_REQ:
			hdd_status = sme_qos_re_request_add_ts(qos_cmd->mac,
					qos_cmd->sessionId,
					&qos_cmd->u.resendCmdInfo.QoSInfo,
					qos_cmd->u.resendCmdInfo.ac,
					qos_cmd->u.resendCmdInfo.tspecMask);
			if (SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP ==
				hdd_status) {
				sme_err("sme_qos_re_request_add_ts failed on session %d",
					session_id);
				qdf_ret_status = QDF_STATUS_E_FAILURE;
			}
			break;
		default:
			sme_err("On session %d unknown cmd = %d",
				session_id, qos_cmd->command);
			break;
		}
		/* buffered command has been processed, reclaim the memory */
		qdf_mem_free(pcmd);
	} else {
		sme_debug("cmd buffer empty");
	}
	return qdf_ret_status;
}

/*
 * sme_qos_delete_buffered_requests() - Utility function to Delete the buffered
 *  requests in the buffered_cmd_list, if we lost connectivity.
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_delete_buffered_requests(struct mac_context *mac,
						   uint8_t sessionId)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_cmdinfoentry *pcmd = NULL;
	tListElem *pEntry = NULL, *pNextEntry = NULL;

	sme_debug("Invoked on session %d", sessionId);
	pSession = &sme_qos_cb.sessionInfo[sessionId];
	pEntry = csr_ll_peek_head(&pSession->bufferedCommandList, true);
	if (!pEntry) {
		sme_debug("Buffered List empty, nothing to delete on session %d",
			  sessionId);
		return QDF_STATUS_E_FAILURE;
	}
	while (pEntry) {
		pNextEntry = csr_ll_next(&pSession->bufferedCommandList, pEntry,
					true);
		sme_debug("deleting entry from buffered List");
		/* delete the entry from Flow List */
		csr_ll_remove_entry(&pSession->bufferedCommandList, pEntry,
				    true);
		/* reclaim the memory */
		pcmd = GET_BASE_ADDR(pEntry, struct sme_qos_cmdinfoentry,
					link);
		qdf_mem_free(pcmd);
		pEntry = pNextEntry;
	}
	return QDF_STATUS_SUCCESS;
}

/**
 * sme_qos_save_assoc_info() - save assoc info.
 * @pSession: pointer to QOS session
 * @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
 *
 * 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.
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS sme_qos_save_assoc_info(struct sme_qos_sessioninfo *pSession,
				   sme_QosAssocInfo *pAssoc_info)
{
	struct bss_description *bss_desc = NULL;
	uint32_t bssLen = 0;

	if (!pAssoc_info) {
		sme_err("pAssoc_info is NULL");
		return QDF_STATUS_E_FAILURE;
	}
	/* clean up the assoc info if already set */
	if (pSession->assocInfo.bss_desc) {
		qdf_mem_free(pSession->assocInfo.bss_desc);
		pSession->assocInfo.bss_desc = NULL;
	}
	bssLen = pAssoc_info->bss_desc->length +
		 sizeof(pAssoc_info->bss_desc->length);
	/* save the bss Descriptor */
	bss_desc = qdf_mem_malloc(bssLen);
	if (!bss_desc)
		return QDF_STATUS_E_NOMEM;

	qdf_mem_copy(bss_desc, pAssoc_info->bss_desc, bssLen);
	pSession->assocInfo.bss_desc = bss_desc;
	/* save the apsd info from assoc */
	pSession->apsdMask |= pAssoc_info->uapsd_mask;

	/* [TODO] Do we need to update the global APSD bitmap? */
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_setup_fnp() - Utility function (pointer) to notify other entries
 *  in FLOW list on the same AC that qos params got modified
 *
 * mac - Pointer to the global MAC parameter structure.
 * pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure)
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_setup_fnp(struct mac_context *mac, tListElem *pEntry)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	enum sme_qos_statustype hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND;
	enum qca_wlan_ac_type ac;

	if (!pEntry) {
		sme_err("Entry is NULL");
		return QDF_STATUS_E_FAILURE;
	}
	flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link);
	ac = flow_info->ac_type;
	pSession = &sme_qos_cb.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(MAC_HANDLE(mac),
				       flow_info->HDDcontext,
				       &pACInfo->curr_QoSInfo[flow_info->
							      tspec_mask - 1],
				       hdd_status, flow_info->QosFlowID);
		sme_debug("Entry with flowID = %d getting notified",
			  flow_info->QosFlowID);
	}
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_modification_notify_fnp() - Utility function (pointer) to notify
 *  other entries in FLOW list on the same AC that qos params got modified
 *
 * mac - Pointer to the global MAC parameter structure.
 * pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure)
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_modification_notify_fnp(struct mac_context *mac, tListElem
					*pEntry)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	enum sme_qos_statustype hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND;
	enum qca_wlan_ac_type ac;

	if (!pEntry) {
		sme_err("Entry is NULL");
		return QDF_STATUS_E_FAILURE;
	}
	flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link);
	ac = flow_info->ac_type;
	pSession = &sme_qos_cb.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(MAC_HANDLE(mac),
				       flow_info->HDDcontext,
				       &pACInfo->curr_QoSInfo[flow_info->
							      tspec_mask - 1],
				       hdd_status, flow_info->QosFlowID);
		sme_debug("Entry with flowID = %d getting notified",
			  flow_info->QosFlowID);
	}
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_modify_fnp() - Utility function (pointer) to delete the original
 *  entry in FLOW list & add the modified one
 *
 * mac - Pointer to the global MAC parameter structure.
 * pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure)
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_modify_fnp(struct mac_context *mac, tListElem *pEntry)
{
	struct sme_qos_flowinfoentry *flow_info = NULL;

	if (!pEntry) {
		sme_err("Entry is NULL");
		return QDF_STATUS_E_FAILURE;
	}
	flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link);

	sme_debug("reason %d", flow_info->reason);
	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 */
		sme_debug("Deleting original entry at %pK with flowID %d",
			  flow_info, flow_info->QosFlowID);
		csr_ll_remove_entry(&sme_qos_cb.flow_list, pEntry, true);
		/* reclaim the memory */
		qdf_mem_free(flow_info);
		break;
	default:
		break;
	}
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_del_ts_ind_fnp() - Utility function (pointer) to find all Flows on
 *  the perticular AC & delete them, also send HDD indication through the
 * callback it registered per request
 *
 * mac - Pointer to the global MAC parameter structure.
 * pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure)
 *
 * Return QDF_STATUS
 */
static QDF_STATUS sme_qos_del_ts_ind_fnp(struct mac_context *mac, tListElem *pEntry)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	enum qca_wlan_ac_type ac;
	QDF_STATUS lock_status = QDF_STATUS_E_FAILURE;
	enum sme_qos_statustype status;

	if (!pEntry) {
		sme_err("Entry is NULL");
		return QDF_STATUS_E_FAILURE;
	}
	/* delete the entry from Flow List */
	flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link);
	ac = flow_info->ac_type;
	pSession = &sme_qos_cb.sessionInfo[flow_info->sessionId];
	pACInfo = &pSession->ac_info[ac];
	pACInfo->relTrig = SME_QOS_RELEASE_BY_AP;

	lock_status = sme_acquire_global_lock(&mac->sme);
	if (!QDF_IS_STATUS_SUCCESS(lock_status)) {
		sme_err("Unable to obtain lock");
		return QDF_STATUS_E_FAILURE;
	}
	/* Call the internal function for QoS release, adding a layer of
	 * abstraction
	 */
	status =
		sme_qos_internal_release_req(mac, flow_info->sessionId,
					     flow_info->QosFlowID, false);
	sme_release_global_lock(&mac->sme);
	sme_debug("QoS Release return status on Flow %d is %d",
		  flow_info->QosFlowID, status);

	return QDF_STATUS_SUCCESS;
}

/**
 * sme_qos_reassoc_success_ev_fnp  Notification function to HDD
 *
 * @mac_ctx: Mac context
 * @entry:   Pointer to an entry in the flow_list
 *
 * 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
 *
 * Return:  QDF_STATUS enumaration
 */
static QDF_STATUS
sme_qos_reassoc_success_ev_fnp(struct mac_context *mac_ctx,
		tListElem *entry)
{
	struct sme_qos_sessioninfo *qos_session;
	struct sme_qos_acinfo *ac_info;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	bool delete_entry = false;
	enum sme_qos_statustype hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
	enum qca_wlan_ac_type ac;
	QDF_STATUS pmc_status = QDF_STATUS_E_FAILURE;

	if (!entry) {
		sme_err("Entry is NULL");
		return QDF_STATUS_E_FAILURE;
	}
	flow_info = GET_BASE_ADDR(entry, struct sme_qos_flowinfoentry, link);
	ac = flow_info->ac_type;
	qos_session = &sme_qos_cb.sessionInfo[flow_info->sessionId];
	ac_info = &qos_session->ac_info[ac];
	switch (flow_info->reason) {
	case SME_QOS_REASON_SETUP:
		hdd_status = SME_QOS_STATUS_SETUP_SUCCESS_IND;
		delete_entry = 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.Notify PMC as App is looking for APSD
		 * If we already requested then we don't need to
		 * do anything.
		 */
		if (ac_info->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0].
				ts_info.psb) {
			/* this is the first flow to detect we need
			 * PMC in UAPSD mode
			 */
			pmc_status = sme_ps_start_uapsd(MAC_HANDLE(mac_ctx),
							flow_info->sessionId);
			/* if PMC doesn't return success right away means
			 * it is yet to put the module in BMPS state & later
			 * to UAPSD state
			 */
			if (QDF_STATUS_E_FAILURE == pmc_status) {
				hdd_status =
					SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_SET_FAILED;
				/* we need to always notify this case */
				flow_info->hoRenewal = false;
			}
		}
		/* for any other pmc status we declare success */
		break;
	case SME_QOS_REASON_RELEASE:
		ac_info->num_flows[SME_QOS_TSPEC_INDEX_0]--;
		fallthrough;
	case SME_QOS_REASON_MODIFY:
		delete_entry = true;
		break;
	case SME_QOS_REASON_MODIFY_PENDING:
		hdd_status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND;
		delete_entry = false;
		flow_info->reason = SME_QOS_REASON_REQ_SUCCESS;
		if (ac_info->requested_QoSInfo[SME_QOS_TSPEC_INDEX_0].
				ts_info.psb) {
			/* this is the first flow to detect we need
			 * PMC in UAPSD mode
			 */
			pmc_status = sme_ps_start_uapsd(MAC_HANDLE(mac_ctx),
							flow_info->sessionId);
			/* if PMC doesn't return success right away means
			 * it is yet to put the module in BMPS state &
			 * later to UAPSD state
			 */
			if (QDF_STATUS_E_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 = false;
			}
		}
		/* for any other pmc status we declare success */
		break;
	case SME_QOS_REASON_REQ_SUCCESS:
		hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND;
		fallthrough;
	default:
		delete_entry = false;
		break;
	}
	if (!delete_entry) {
		if (!flow_info->hoRenewal) {
			flow_info->QoSCallback(MAC_HANDLE(mac_ctx),
					       flow_info->HDDcontext,
					       &ac_info->curr_QoSInfo[SME_QOS_TSPEC_INDEX_0],
					       hdd_status,
					       flow_info->QosFlowID);
		} else
			flow_info->hoRenewal = false;
	} else {
		/* delete the entry from Flow List */
		sme_debug("Deleting entry at %pK with flowID %d",
			  flow_info, flow_info->QosFlowID);
		csr_ll_remove_entry(&sme_qos_cb.flow_list, entry, true);
		/* reclaim the memory */
		qdf_mem_free(flow_info);
	}
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_add_ts_failure_fnp() - 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
 *
 * mac - Pointer to the global MAC parameter structure.
 * pEntry - Pointer to an entry in the flow_list(i.e. tListElem structure)
 *
 *  Return QDF_STATUS
 */
static QDF_STATUS sme_qos_add_ts_failure_fnp(struct mac_context *mac, tListElem
						*pEntry)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	bool inform_hdd = false;
	enum sme_qos_statustype hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
	enum qca_wlan_ac_type ac;

	if (!pEntry) {
		sme_err("Entry is NULL");
		return QDF_STATUS_E_FAILURE;
	}
	flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry, link);
	ac = flow_info->ac_type;
	pSession = &sme_qos_cb.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 = true;
		break;
	case SME_QOS_REASON_RELEASE:
		hdd_status = SME_QOS_STATUS_RELEASE_FAILURE_RSP;
		pACInfo->num_flows[pACInfo->tspec_pending - 1]--;
		inform_hdd = true;
		break;
	case SME_QOS_REASON_MODIFY_PENDING:
		hdd_status = SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP;
		inform_hdd = true;
		break;
	case SME_QOS_REASON_MODIFY:
		flow_info->reason = SME_QOS_REASON_REQ_SUCCESS;
		fallthrough;
	case SME_QOS_REASON_REQ_SUCCESS:
		fallthrough;
	default:
		inform_hdd = 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(MAC_HANDLE(mac),
					       flow_info->HDDcontext,
					       &pACInfo->curr_QoSInfo[pACInfo->
								   tspec_pending
								      - 1],
					       hdd_status,
					       flow_info->QosFlowID);
		} else {
			flow_info->QoSCallback(MAC_HANDLE(mac),
					       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 */
		sme_debug("Deleting entry at %pK with flowID %d",
			  flow_info, flow_info->QosFlowID);
		csr_ll_remove_entry(&sme_qos_cb.flow_list, pEntry, true);
		/* reclaim the memory */
		qdf_mem_free(flow_info);
	}
	return QDF_STATUS_SUCCESS;
}

/**
 * sme_qos_add_ts_success_fnp() - Utility function (pointer) to notify HDD
 *
 * @mac_ctx: Mac context
 * @entry:   Pointer to an entry in the flow_list(i.e. tListElem structure).
 *
 * Description : 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
 *
 * Return: Status
 */

static QDF_STATUS sme_qos_add_ts_success_fnp(struct mac_context *mac_ctx,
		tListElem *entry)
{
	struct sme_qos_sessioninfo *qos_session;
	struct sme_qos_acinfo *ac_info;
	struct sme_qos_flowinfoentry *flow_info = NULL;
	bool inform_hdd = false;
	bool delete_entry = false;
	enum sme_qos_statustype hdd_status = SME_QOS_STATUS_SETUP_FAILURE_RSP;
	enum qca_wlan_ac_type ac;
	QDF_STATUS pmc_status = QDF_STATUS_E_FAILURE;
	tCsrRoamModifyProfileFields profile_fields;
	uint8_t psb;
	uint8_t tspec_index;

	if (!entry) {
		sme_err("Entry is NULL");
		return QDF_STATUS_E_FAILURE;
	}
	flow_info = GET_BASE_ADDR(entry, struct sme_qos_flowinfoentry, link);
	ac = flow_info->ac_type;
	qos_session = &sme_qos_cb.sessionInfo[flow_info->sessionId];
	ac_info = &qos_session->ac_info[ac];
	tspec_index = ac_info->tspec_pending - 1;
	if (flow_info->tspec_mask != ac_info->tspec_pending) {
		sme_debug(" No need to notify the HDD, the ADDTS success is not for index = %d of the AC = %d",
			  flow_info->tspec_mask, ac);
		return QDF_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 = false;
		inform_hdd = true;
		/* check if App is looking for APSD
		 * notify PMC as App is looking for APSD. If we already
		 * requested then we don't need to do anything
		 */
		if (ac_info->requested_QoSInfo[tspec_index].ts_info.psb) {
			/* this is the first flow to detect we need
			 * PMC in UAPSD mode
			 */
			pmc_status = sme_ps_start_uapsd(MAC_HANDLE(mac_ctx),
							flow_info->sessionId);
			/* if PMC doesn't return success right away means
			 * it is yet to put the module in BMPS state & later
			 * to UAPSD state
			 */
			if (QDF_STATUS_E_FAILURE == pmc_status) {
				hdd_status =
					SME_QOS_STATUS_SETUP_SUCCESS_IND_APSD_SET_FAILED;
				/* we need to always notify this case */
				flow_info->hoRenewal = false;
			}
		}
		break;
	case SME_QOS_REASON_RELEASE:
		ac_info->num_flows[tspec_index]--;
		hdd_status = SME_QOS_STATUS_RELEASE_SUCCESS_RSP;
		inform_hdd = true;
		delete_entry = true;
		break;
	case SME_QOS_REASON_MODIFY:
		delete_entry = true;
		inform_hdd = false;
		break;
	case SME_QOS_REASON_MODIFY_PENDING:
		hdd_status = SME_QOS_STATUS_MODIFY_SETUP_SUCCESS_IND;
		delete_entry = false;
		flow_info->reason = SME_QOS_REASON_REQ_SUCCESS;
		inform_hdd = true;
		psb = ac_info->requested_QoSInfo[tspec_index].ts_info.psb;
		/* notify PMC if App is looking for APSD
		 */
		if (psb) {
			/* this is the first flow to detect
			 * we need PMC in UAPSD mode
			 */
			pmc_status = sme_ps_start_uapsd(MAC_HANDLE(mac_ctx),
							flow_info->sessionId);
			/* if PMC doesn't return success right
			 * away means it is yet to put
			 * the module in BMPS state & later to UAPSD state
			 */
			if (QDF_STATUS_E_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 = false;
			}
		} else if (!psb &&
		((ac_info->num_flows[flow_info->tspec_mask - 1] == 1)
			    && (SME_QOS_TSPEC_MASK_BIT_1_2_SET !=
			ac_info->tspec_mask_status))) {
			/* this is the only TSPEC active on this AC */
			/* so indicate that we no longer require APSD */
			qos_session->apsdMask &=
				~(1 << (QCA_WLAN_AC_VO - ac));
			/* Also update modifyProfileFields.uapsd_mask
			 * in CSR for consistency
			 */
			csr_get_modify_profile_fields(mac_ctx,
				flow_info->sessionId,
				&profile_fields);
			profile_fields.uapsd_mask =
				qos_session->apsdMask;
			csr_set_modify_profile_fields(mac_ctx,
				flow_info->sessionId,
				&profile_fields);
			if (!qos_session->apsdMask)
				sme_ps_uapsd_disable(MAC_HANDLE(mac_ctx),
					flow_info->sessionId);
		}
		break;
	case SME_QOS_REASON_REQ_SUCCESS:
		hdd_status = SME_QOS_STATUS_SETUP_MODIFIED_IND;
		inform_hdd = true;
		fallthrough;
	default:
		delete_entry = false;
		break;
	}
	if (inform_hdd) {
		if (!flow_info->hoRenewal) {
			flow_info->QoSCallback(MAC_HANDLE(mac_ctx),
					       flow_info->HDDcontext,
					       &ac_info->curr_QoSInfo[tspec_index],
					       hdd_status,
					       flow_info->QosFlowID);
		} else
			flow_info->hoRenewal = false;
	}
	if (delete_entry) {
		sme_debug("Deleting entry at %pK with flowID %d",
			  flow_info, flow_info->QosFlowID);
		/* delete the entry from Flow List */
		csr_ll_remove_entry(&sme_qos_cb.flow_list, entry, true);
		/* reclaim the memory */
		qdf_mem_free(flow_info);
	}
	return QDF_STATUS_SUCCESS;
}

/*
 * sme_qos_is_rsp_pending() - Utility function to check if we are waiting
 *  for an AddTS or reassoc response on some AC other than the given AC
 *
 * sessionId - Session we are interted in
 * ac - Enumeration of the various EDCA Access Categories.
 *
 * Return bool
 *  true - Response is pending on an AC
 */
static bool sme_qos_is_rsp_pending(uint8_t sessionId, enum qca_wlan_ac_type ac)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	enum qca_wlan_ac_type acIndex;
	bool status = false;

	pSession = &sme_qos_cb.sessionInfo[sessionId];
	for (acIndex = QCA_WLAN_AC_BE; acIndex < QCA_WLAN_AC_ALL;
	     acIndex++) {
		if (acIndex == ac)
			continue;
		pACInfo = &pSession->ac_info[acIndex];
		if ((pACInfo->tspec_pending) || (pACInfo->reassoc_pending)) {
			status = true;
			break;
		}
	}
	return status;
}

/*
 * sme_qos_update_hand_off() - Function which can be called to update
 *  Hand-off state of SME QoS Session
 *
 * sessionId - session id
 * updateHandOff - value True/False to update the handoff flag
 */
void sme_qos_update_hand_off(uint8_t sessionId, bool updateHandOff)
{
	struct sme_qos_sessioninfo *pSession;

	pSession = &sme_qos_cb.sessionInfo[sessionId];
	sme_debug("handoffRequested %d updateHandOff %d",
		  pSession->handoffRequested, updateHandOff);

	pSession->handoffRequested = updateHandOff;

}

/*
 * sme_qos_is_uapsd_active() - Function which can be called to determine
 *  if any sessions require PMC to be in U-APSD mode.
 * Return bool
 *
 *  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
 */
static bool sme_qos_is_uapsd_active(void)
{
	struct sme_qos_sessioninfo *pSession;
	uint8_t sessionId;

	for (sessionId = 0; sessionId < WLAN_MAX_VDEVS; ++sessionId) {
		pSession = &sme_qos_cb.sessionInfo[sessionId];
		if ((pSession->sessionActive) && (pSession->apsdMask))
			return true;
	}
	/* no active sessions have U-APSD active */
	return false;
}

QDF_STATUS sme_offload_qos_process_out_of_uapsd_mode(struct mac_context *mac,
						     uint32_t sessionId)
{
	struct sme_qos_sessioninfo *pSession;
	tListElem *pEntry = NULL, *pNextEntry = NULL;
	struct sme_qos_flowinfoentry *flow_info = NULL;

	pEntry = csr_ll_peek_head(&sme_qos_cb.flow_list, false);
	if (!pEntry) {
		sme_debug("Flow List empty, can't search");
		return QDF_STATUS_E_FAILURE;
	}
	while (pEntry) {
		pNextEntry = csr_ll_next(&sme_qos_cb.flow_list, pEntry, false);
		flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry,
					link);
		pSession = &sme_qos_cb.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(MAC_HANDLE(mac),
					       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 QDF_STATUS_SUCCESS;
}

QDF_STATUS sme_offload_qos_process_into_uapsd_mode(struct mac_context *mac,
						   uint32_t sessionId)
{
	struct sme_qos_sessioninfo *pSession;
	tListElem *pEntry = NULL, *pNextEntry = NULL;
	struct sme_qos_flowinfoentry *flow_info = NULL;

	pEntry = csr_ll_peek_head(&sme_qos_cb.flow_list, false);
	if (!pEntry) {
		sme_err("Flow List empty, can't search");
		return QDF_STATUS_E_FAILURE;
	}
	while (pEntry) {
		pNextEntry = csr_ll_next(&sme_qos_cb.flow_list, pEntry, false);
		flow_info = GET_BASE_ADDR(pEntry, struct sme_qos_flowinfoentry,
					link);
		pSession = &sme_qos_cb.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(MAC_HANDLE(mac),
					       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 QDF_STATUS_SUCCESS;
}

void sme_qos_cleanup_ctrl_blk_for_handoff(struct mac_context *mac,
					uint8_t sessionId)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	enum qca_wlan_ac_type ac;

	pSession = &sme_qos_cb.sessionInfo[sessionId];
	sme_debug("invoked on session %d", sessionId);

	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
		pACInfo = &pSession->ac_info[ac];
		qdf_mem_zero(pACInfo->curr_QoSInfo,
			     sizeof(struct sme_qos_wmmtspecinfo) *
			     SME_QOS_TSPEC_INDEX_MAX);
		qdf_mem_zero(pACInfo->requested_QoSInfo,
			     sizeof(struct sme_qos_wmmtspecinfo) *
			     SME_QOS_TSPEC_INDEX_MAX);
		pACInfo->num_flows[0] = 0;
		pACInfo->num_flows[1] = 0;
		pACInfo->reassoc_pending = false;
		pACInfo->tspec_mask_status = 0;
		pACInfo->tspec_pending = false;
		pACInfo->hoRenewal = false;
		pACInfo->prev_state = SME_QOS_LINK_UP;
	}
}

/**
 * sme_qos_is_ts_info_ack_policy_valid() - check if ACK policy is allowed.
 * @mac_handle: The handle returned by mac_open.
 * @pQoSInfo: Pointer to struct sme_qos_wmmtspecinfo which contains the
 *            WMM TSPEC related info, provided by HDD
 * @sessionId: sessionId returned by sme_open_session.
 *
 * The SME QoS API exposed to HDD to check if TS info ack policy field can be
 * set to "HT-immediate block acknowledgment"
 *
 * Return: true - Current Association is HT association and so TS info ack
 *                 policy can be set to "HT-immediate block acknowledgment"
 */
bool sme_qos_is_ts_info_ack_policy_valid(mac_handle_t mac_handle,
					 struct sme_qos_wmmtspecinfo *pQoSInfo,
					 uint8_t sessionId)
{
	tDot11fBeaconIEs *pIes = NULL;
	struct sme_qos_sessioninfo *pSession;
	QDF_STATUS hstatus;
	struct mac_context *mac = MAC_CONTEXT(mac_handle);

	if (!CSR_IS_SESSION_VALID(mac, sessionId)) {
		sme_err("Session Id %d is invalid", sessionId);
		return false;
	}

	pSession = &sme_qos_cb.sessionInfo[sessionId];

	if (!pSession->sessionActive) {
		sme_err("Session %d is inactive", sessionId);
		return false;
	}

	if (!pSession->assocInfo.bss_desc) {
		sme_err("Session %d has an Invalid BSS Descriptor", sessionId);
		return false;
	}

	hstatus = csr_get_parsed_bss_description_ies(mac,
						   pSession->assocInfo.bss_desc,
						      &pIes);
	if (!QDF_IS_STATUS_SUCCESS(hstatus)) {
		sme_err("On session %d unable to parse BSS IEs", sessionId);
		return 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) {
		sme_err("On session %d HT Caps aren't present but application set ack policy to HT ",
			sessionId);

		qdf_mem_free(pIes);
		return false;
	}

	qdf_mem_free(pIes);
	return true;
}

static bool sme_qos_validate_requested_params(struct mac_context *mac,
				       struct sme_qos_wmmtspecinfo *qos_info,
				       uint8_t session_id)
{
	if (SME_QOS_WMM_TS_DIR_RESV == qos_info->ts_info.direction)
		return false;
	if (!sme_qos_is_ts_info_ack_policy_valid(MAC_HANDLE(mac),
						 qos_info, session_id))
		return false;

	return true;
}

static QDF_STATUS qos_issue_command(struct mac_context *mac, uint8_t vdev_id,
				    eSmeCommandType cmdType,
				    struct sme_qos_wmmtspecinfo *pQoSInfo,
				    enum qca_wlan_ac_type ac, uint8_t tspec_mask)
{
	QDF_STATUS status = QDF_STATUS_E_RESOURCES;
	tSmeCmd *pCommand = NULL;

	do {
		pCommand = csr_get_command_buffer(mac);
		if (!pCommand) {
			sme_err("fail to get command buffer for command %d",
				cmdType);
			break;
		}
		pCommand->command = cmdType;
		pCommand->vdev_id = vdev_id;
		switch (cmdType) {
		case eSmeCommandAddTs:
			if (pQoSInfo) {
				status = QDF_STATUS_SUCCESS;
				pCommand->u.qosCmd.tspecInfo = *pQoSInfo;
				pCommand->u.qosCmd.ac = ac;
			} else {
				sme_err("NULL pointer passed");
				status = QDF_STATUS_E_INVAL;
			}
			break;
		case eSmeCommandDelTs:
			status = QDF_STATUS_SUCCESS;
			pCommand->u.qosCmd.ac = ac;
			pCommand->u.qosCmd.tspec_mask = tspec_mask;
			break;
		default:
			sme_err("invalid command type %d", cmdType);
			status = QDF_STATUS_E_INVAL;
			break;
		}
	} while (0);
	if (QDF_IS_STATUS_SUCCESS(status) && pCommand)
		csr_queue_sme_command(mac, pCommand, false);
	else if (pCommand)
		qos_release_command(mac, pCommand);

	return status;
}

bool qos_process_command(struct mac_context *mac, tSmeCmd *pCommand)
{
	QDF_STATUS status = QDF_STATUS_SUCCESS;
	bool fRemoveCmd = true;

	do {
		switch (pCommand->command) {
		case eSmeCommandAddTs:
			status =
				sme_qos_add_ts_req(mac, (uint8_t)
						pCommand->vdev_id,
						  &pCommand->u.qosCmd.tspecInfo,
						   pCommand->u.qosCmd.ac);
			if (QDF_IS_STATUS_SUCCESS(status))
				fRemoveCmd = false;
			break;
		case eSmeCommandDelTs:
			status =
				sme_qos_del_ts_req(mac, (uint8_t)
						pCommand->vdev_id,
						   pCommand->u.qosCmd.ac,
						 pCommand->u.qosCmd.tspec_mask);
			if (QDF_IS_STATUS_SUCCESS(status))
				fRemoveCmd = false;
			break;
		default:
			sme_err("invalid command type %d", pCommand->command);
			break;
		} /* switch */
	} while (0);
	return fRemoveCmd;
}

/**
 * sme_qos_re_request_add_ts - Re-send AddTS for the combined QoS request
 *
 * @mac_ctx  Pointer to mac context
 * @session_id  SME session id
 * @qos_info - Tspec information
 * @ac - Access category
 * @tspec_mask - Tspec Mask
 *
 * This function is called to re-send AddTS for the combined QoS request
 *
 * Return: status
 */
static
enum sme_qos_statustype sme_qos_re_request_add_ts(struct mac_context *mac_ctx,
		uint8_t session_id, struct sme_qos_wmmtspecinfo *qos_info,
		enum qca_wlan_ac_type ac, uint8_t tspec_mask)
{
	struct sme_qos_sessioninfo *session;
	struct sme_qos_acinfo *ac_info;
	enum sme_qos_statustype status =
		SME_QOS_STATUS_MODIFY_SETUP_FAILURE_RSP;
	struct sme_qos_cmdinfo cmd;

	sme_debug(" Invoked on session %d for AC %d TSPEC %d", session_id, ac,
		  tspec_mask);
	session = &sme_qos_cb.sessionInfo[session_id];
	ac_info = &session->ac_info[ac];
	/*
	 * call PMC's request for power function
	 * AND another check is added considering the 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,
	 * 2& also won't confuse PE with back to back Addts or Addts
	 * followed by Reassoc.
	 */
	if (sme_qos_is_rsp_pending(session_id, ac)) {
		sme_err("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",
			session_id, ac, ac_info->curr_state);
		/* buffer cmd */
		cmd.command = SME_QOS_RESEND_REQ;
		cmd.mac = mac_ctx;
		cmd.sessionId = session_id;
		cmd.u.resendCmdInfo.ac = ac;
		cmd.u.resendCmdInfo.tspecMask = tspec_mask;
		cmd.u.resendCmdInfo.QoSInfo = *qos_info;
		if (!QDF_IS_STATUS_SUCCESS(sme_qos_buffer_cmd(&cmd, false))) {
			sme_err("On session %d unable to buffer the AddTS request for AC %d TSPEC %d in state %d",
				session_id, ac, tspec_mask,
				ac_info->curr_state);
			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 (ac_info->curr_state) {
	case SME_QOS_QOS_ON:
	{
		/* if ACM, send out a new ADDTS */
		ac_info->hoRenewal = true;
		status = sme_qos_setup(mac_ctx, session_id, qos_info, ac);
		sme_debug("sme_qos_setup returned in SME_QOS_QOS_ON state sme_qos_setup AC %d with status =%d",
			  ac, status);
		if (SME_QOS_STATUS_SETUP_REQ_PENDING_RSP == status) {
			status = SME_QOS_STATUS_MODIFY_SETUP_PENDING_RSP;
			ac_info->tspec_pending = tspec_mask;
		} 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)) {
			sme_err("UAPSD is setup already status = %d ", status);
		} else {
			sme_err("sme_qos_setup return status = %d ", status);
		}
	}
	break;
	case SME_QOS_HANDOFF:
	case SME_QOS_REQUESTED:
		sme_err("Re-Add request in state = %d  buffer the request",
			ac_info->curr_state);
		cmd.command = SME_QOS_RESEND_REQ;
		cmd.mac = mac_ctx;
		cmd.sessionId = session_id;
		cmd.u.resendCmdInfo.ac = ac;
		cmd.u.resendCmdInfo.tspecMask = tspec_mask;
		cmd.u.resendCmdInfo.QoSInfo = *qos_info;
		if (!QDF_IS_STATUS_SUCCESS(sme_qos_buffer_cmd(&cmd, false))) {
			sme_err(" couldn't buf the read request state = %d",
				ac_info->curr_state);
			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, */
		sme_err("ReAdd request in unexpected state = %d",
			ac_info->curr_state);
		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_qos_process_buffered_cmd(session_id);

	return status;
}

static void sme_qos_init_a_cs(struct mac_context *mac, uint8_t sessionId)
{
	struct sme_qos_sessioninfo *pSession;
	enum qca_wlan_ac_type ac;

	pSession = &sme_qos_cb.sessionInfo[sessionId];
	for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
		qdf_mem_zero(&pSession->ac_info[ac],
				sizeof(struct sme_qos_acinfo));
		sme_qos_state_transition(sessionId, ac, SME_QOS_INIT);
	}
}

static QDF_STATUS sme_qos_request_reassoc(struct mac_context *mac,
					uint8_t sessionId,
					  tCsrRoamModifyProfileFields *
					  pModFields, bool fForce)
{
	struct sme_qos_sessioninfo *pSession;
	struct sme_qos_acinfo *pACInfo;
	QDF_STATUS status;
	struct qdf_mac_addr bssid;
	qdf_freq_t ch_freq;

	sme_debug("Invoked on session %d with UAPSD mask 0x%X",
		  sessionId, pModFields->uapsd_mask);

	if (!CSR_IS_SESSION_VALID(mac, sessionId)) {
		sme_err("Invalid session for sessionId: %d", sessionId);
		return QDF_STATUS_E_FAILURE;
	}

	pSession = &sme_qos_cb.sessionInfo[sessionId];
	wlan_mlme_get_bssid_vdev_id(mac->pdev, sessionId, &bssid);
	ch_freq = wlan_get_operation_chan_freq_vdev_id(mac->pdev, sessionId);
	status = wlan_cm_roam_invoke(mac->pdev, sessionId, &bssid, ch_freq,
				     CM_ROAMING_HOST);

	if (QDF_IS_STATUS_SUCCESS(status)) {
		/* Update the state to Handoff so subsequent requests are
		 * queued until this one is finished
		 */
		enum qca_wlan_ac_type ac;

		for (ac = QCA_WLAN_AC_BE; ac < QCA_WLAN_AC_ALL; ac++) {
			pACInfo = &pSession->ac_info[ac];
			sme_debug("AC[%d] is in state [%d]",
				  ac, pACInfo->curr_state);
			/* If it is already in HANDOFF state, don't do
			 * anything since we MUST preserve the previous state
			 * and sme_qos_state_transition will change the previous
			 * state
			 */
			if (SME_QOS_HANDOFF != pACInfo->curr_state)
				sme_qos_state_transition(sessionId, ac,
							 SME_QOS_HANDOFF);
		}
	}
	return status;
}

static uint32_t sme_qos_assign_flow_id(void)
{
	uint32_t flowId;

	flowId = sme_qos_cb.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
		 */
		sme_debug("Software Test made the flow counter wrap, QoS may no longer be functional");
		sme_qos_cb.nextFlowId = SME_QOS_MIN_FLOW_ID;
	} else
		sme_qos_cb.nextFlowId++;

	return flowId;
}

static uint8_t sme_qos_assign_dialog_token(void)
{
	uint8_t token;

	token = sme_qos_cb.nextDialogToken;
	if (SME_QOS_MAX_DIALOG_TOKEN == token)
		/* wrap is ok */
		sme_qos_cb.nextDialogToken = SME_QOS_MIN_DIALOG_TOKEN;
	else
		sme_qos_cb.nextDialogToken++;

	sme_debug("token %d", token);
	return token;
}
#endif /* WLAN_MDM_CODE_REDUCTION_OPT */
