Browse Source

qcacld-3.0: Add support for T2LM action frame handling

Add support for T2LM action frame handling.
- Add an API to parse T2LM negotiation request action frame
and accept or reject T2LM mapping based on ML link info.
If accepted, store the new info in peer level context
and send it to FW.
- Add an API to send T2LM response action frame to
acknowledge request action frame with status code.
- Add wrapper API to invoke T2LM deliver event.

Change-Id: I10d440bd1713740ab8b9f1fe46439bea92e179db
CRs-Fixed: 3367425
Gururaj Pandurangi 2 years ago
parent
commit
8859328749

+ 50 - 23
components/umac/mlme/mlo_mgr/inc/wlan_t2lm_api.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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
@@ -60,21 +60,27 @@ enum wlan_t2lm_evt {
 /**
  * t2lm_deliver_event - Handler to deliver T2LM event
  * @vdev: vdev pointer
+ * @peer: pointer to peer
  * @event: T2LM event
  * @event_data: T2LM event data pointer
+ * @dialog_token: Dialog token
  *
  * This api will be called from lim  layers, to process T2LM event
  *
  * Return: qdf_status
  */
 QDF_STATUS t2lm_deliver_event(struct wlan_objmgr_vdev *vdev,
+			      struct wlan_objmgr_peer *peer,
 			      enum wlan_t2lm_evt event,
-			      struct t2lm_event_data *event_data);
+			      void *event_data,
+			      uint8_t *dialog_token);
 
 /**
  * t2lm_handle_rx_req - Handler for parsing T2LM action frame
  * @vdev: vdev pointer
+ * @peer: pointer to peer
  * @event_data: T2LM event data pointer
+ * @token: Dialog token
  *
  * This api will be called from lim  layers, after T2LM action frame
  * is received, the api will parse the T2LM request frame.
@@ -82,43 +88,47 @@ QDF_STATUS t2lm_deliver_event(struct wlan_objmgr_vdev *vdev,
  * Return: qdf_status
  */
 QDF_STATUS t2lm_handle_rx_req(struct wlan_objmgr_vdev *vdev,
-			      struct t2lm_event_data *event_data);
+			      struct wlan_objmgr_peer *peer,
+			      void *event_data, uint8_t *token);
 
 /**
  * t2lm_handle_tx_resp - Handler for populating T2LM action frame
  * @vdev: vdev pointer
  * @event_data: T2LM event data pointer
+ * @token: Dialog token
  *
  * This api will be called to populate T2LM response action frame.
  *
  * Return: qdf_status
  */
 QDF_STATUS t2lm_handle_tx_resp(struct wlan_objmgr_vdev *vdev,
-			       struct t2lm_event_data *event_data);
+			       void *event_data, uint8_t *token);
 
 /**
  * t2lm_handle_tx_req - Handler for populating T2LM action frame
  * @vdev: vdev pointer
  * @event_data: T2LM event data pointer
+ * @token: Dialog token
  *
  * This api will be called to populate T2LM request action frame.
  *
  * Return: qdf_status
  */
 QDF_STATUS t2lm_handle_tx_req(struct wlan_objmgr_vdev *vdev,
-			      struct t2lm_event_data *event_data);
+			      void *event_data, uint8_t *token);
 
 /**
  * t2lm_handle_rx_resp - Handler for parsing T2LM action frame
  * @vdev: vdev pointer
  * @event_data: T2LM event data pointer
+ * @token: Dialog token
  *
  * This api will be called to parsing T2LM response action frame.
  *
  * Return: qdf_status
  */
 QDF_STATUS t2lm_handle_rx_resp(struct wlan_objmgr_vdev *vdev,
-			       struct t2lm_event_data *event_data);
+			       void *event_data, uint8_t *token);
 
 /**
  * t2lm_handle_rx_teardown - Handler for parsing T2LM action frame
@@ -130,7 +140,7 @@ QDF_STATUS t2lm_handle_rx_resp(struct wlan_objmgr_vdev *vdev,
  * Return: qdf_status
  */
 QDF_STATUS t2lm_handle_rx_teardown(struct wlan_objmgr_vdev *vdev,
-				   struct t2lm_event_data *event_data);
+				   void *event_data);
 
 /**
  * t2lm_handle_tx_teardown - Handler for populating T2LM action frame
@@ -142,7 +152,7 @@ QDF_STATUS t2lm_handle_rx_teardown(struct wlan_objmgr_vdev *vdev,
  * Return: qdf_status
  */
 QDF_STATUS t2lm_handle_tx_teardown(struct wlan_objmgr_vdev *vdev,
-				   struct t2lm_event_data *event_data);
+				   void *event_data);
 
 /**
  * wlan_t2lm_validate_candidate - Validate candidate based on T2LM IE
@@ -158,54 +168,61 @@ QDF_STATUS t2lm_handle_tx_teardown(struct wlan_objmgr_vdev *vdev,
 QDF_STATUS
 wlan_t2lm_validate_candidate(struct cnx_mgr *cm_ctx,
 			     struct scan_cache_entry *scan_entry);
-
+/**
+ * wlan_t2lm_deliver_event() - TID-to-link-mapping event handler
+ * @vdev: vdev object
+ * @peer: pointer to peer
+ * @event: T2LM event
+ * @event_data: T2LM event data
+ * @dialog_token: Dialog token
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wlan_t2lm_deliver_event(struct wlan_objmgr_vdev *vdev,
+				   struct wlan_objmgr_peer *peer,
+				   enum wlan_t2lm_evt event,
+				   void *event_data,
+				   uint8_t *dialog_token);
 #else
-static inline QDF_STATUS
-t2lm_deliver_event(struct wlan_objmgr_vdev *vdev,
-		   enum wlan_t2lm_evt event,
-		   struct t2lm_event_data *event_data)
-{
-	return QDF_STATUS_E_NOSUPPORT;
-}
-
 static inline QDF_STATUS
 t2lm_handle_rx_req(struct wlan_objmgr_vdev *vdev,
-		   struct t2lm_event_data *event_data)
+		   struct wlan_objmgr_peer *peer,
+		   void *event_data, uint8_t *token)
 {
 	return QDF_STATUS_E_NOSUPPORT;
 }
 
 static inline QDF_STATUS
 t2lm_handle_tx_resp(struct wlan_objmgr_vdev *vdev,
-		    struct t2lm_event_data *event_data)
+		    void *event_data, uint8_t *token)
 {
 	return QDF_STATUS_E_NOSUPPORT;
 }
 
 static inline QDF_STATUS
 t2lm_handle_tx_req(struct wlan_objmgr_vdev *vdev,
-		   struct t2lm_event_data *event_data)
+		   void *event_data, uint8_t *token)
 {
 	return QDF_STATUS_E_NOSUPPORT;
 }
 
 static inline QDF_STATUS
 t2lm_handle_rx_resp(struct wlan_objmgr_vdev *vdev,
-		    struct t2lm_event_data *event_data)
+		    void *event_data, uint8_t *token)
 {
 	return QDF_STATUS_E_NOSUPPORT;
 }
 
 static inline QDF_STATUS
 t2lm_handle_rx_teardown(struct wlan_objmgr_vdev *vdev,
-			struct t2lm_event_data *event_data)
+			void *event_data)
 {
 	return QDF_STATUS_E_NOSUPPORT;
 }
 
 static inline QDF_STATUS
 t2lm_handle_tx_teardown(struct wlan_objmgr_vdev *vdev,
-			struct t2lm_event_data *event_data)
+			void *event_data)
 {
 	return QDF_STATUS_E_NOSUPPORT;
 }
@@ -216,5 +233,15 @@ wlan_t2lm_validate_candidate(struct cnx_mgr *cm_ctx,
 {
 	return QDF_STATUS_E_NOSUPPORT;
 }
+
+static inline
+QDF_STATUS wlan_t2lm_deliver_event(struct wlan_objmgr_vdev *vdev,
+				   struct wlan_objmgr_peer *peer,
+				   enum wlan_t2lm_evt event,
+				   void *event_data,
+				   uint8_t *dialog_token)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
 #endif
 #endif

+ 136 - 37
components/umac/mlme/mlo_mgr/src/wlan_t2lm_api.c

@@ -21,6 +21,7 @@
 #include <wlan_cm_public_struct.h>
 #include "wlan_t2lm_api.h"
 #include <wlan_mlo_t2lm.h>
+#include "wlan_cm_api.h"
 
 static
 const char *t2lm_get_event_str(enum wlan_t2lm_evt event)
@@ -40,70 +41,159 @@ const char *t2lm_get_event_str(enum wlan_t2lm_evt event)
 	}
 }
 
-QDF_STATUS t2lm_handle_rx_req(struct wlan_objmgr_vdev *vdev,
-			      struct t2lm_event_data *event_data)
+static
+bool t2lm_is_valid_t2lm_link_map(struct wlan_objmgr_vdev *vdev,
+				 struct wlan_t2lm_onging_negotiation_info *t2lm,
+				 uint8_t *valid_dir)
 {
-	return QDF_STATUS_SUCCESS;
+	uint8_t i, tid = 0;
+	enum wlan_t2lm_direction dir = WLAN_T2LM_INVALID_DIRECTION;
+	uint16_t ieee_link_mask = 0;
+	uint16_t provisioned_links = 0;
+	bool is_valid_link_mask = false;
+	struct wlan_objmgr_vdev *ml_vdev = NULL;
+	struct wlan_objmgr_vdev *ml_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = {NULL};
+	uint16_t ml_vdev_cnt = 0;
+
+	/* Get the valid hw_link_id map from ML vdev list */
+	mlo_get_ml_vdev_list(vdev, &ml_vdev_cnt, ml_vdev_list);
+	if (!ml_vdev_cnt) {
+		t2lm_err("Number of VDEVs under MLD is reported as 0");
+		return false;
+	}
+
+	for (i = 0; i < ml_vdev_cnt; i++) {
+		ml_vdev = ml_vdev_list[i];
+		if (!ml_vdev || !wlan_cm_is_vdev_connected(ml_vdev)) {
+			t2lm_err("ML vdev is null");
+			continue;
+		}
+
+		ieee_link_mask |= BIT(wlan_vdev_get_link_id(ml_vdev));
+	}
+
+	if (ml_vdev_cnt) {
+		for (i = 0; i < ml_vdev_cnt; i++)
+			mlo_release_vdev_ref(ml_vdev_list[i]);
+	}
+
+	/* Check if the configured hw_link_id map is valid */
+	for (dir = 0; dir < WLAN_T2LM_MAX_DIRECTION; dir++) {
+		if (t2lm->t2lm_info[dir].direction ==
+		    WLAN_T2LM_INVALID_DIRECTION)
+			continue;
+
+		if (t2lm->t2lm_info[dir].default_link_mapping) {
+			is_valid_link_mask = true;
+			continue;
+		}
+
+		for (tid = 0; tid < T2LM_MAX_NUM_TIDS; tid++) {
+			provisioned_links =
+				t2lm->t2lm_info[dir].ieee_link_map_tid[tid];
+
+			for (i = 0; i < WLAN_T2LM_MAX_NUM_LINKS; i++) {
+				if (!(provisioned_links & BIT(i)))
+					continue;
+
+				if (ieee_link_mask & BIT(i)) {
+					is_valid_link_mask = true;
+					*valid_dir = dir;
+					continue;
+				} else {
+					return false;
+				}
+			}
+		}
+	}
+
+	return is_valid_link_mask;
 }
 
-QDF_STATUS t2lm_handle_tx_resp(struct wlan_objmgr_vdev *vdev,
-			       struct t2lm_event_data *event_data)
+QDF_STATUS t2lm_handle_rx_req(struct wlan_objmgr_vdev *vdev,
+			      struct wlan_objmgr_peer *peer,
+			      void *event_data, uint8_t *token)
 {
-	if (!vdev) {
-		mlme_err("vdev is NULL");
-		return QDF_STATUS_E_NULL_VALUE;
+	struct wlan_t2lm_onging_negotiation_info t2lm_req = {0};
+	struct wlan_t2lm_info *t2lm_info;
+	uint8_t dir = WLAN_T2LM_MAX_DIRECTION;
+	bool valid_map = false;
+	QDF_STATUS status;
+	struct wlan_mlo_peer_context *ml_peer;
+
+	ml_peer = peer->mlo_peer_ctx;
+	if (!ml_peer)
+		return QDF_STATUS_E_FAILURE;
+
+	status = wlan_mlo_parse_t2lm_action_frame(&t2lm_req, event_data,
+						  WLAN_T2LM_CATEGORY_REQUEST);
+	if (status != QDF_STATUS_SUCCESS) {
+		mlme_err("Unable to parse T2LM request action frame");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	/*
+	 * Check if ML vdevs are connected and link id matches with T2LM
+	 * negotiation action request link id
+	 */
+	valid_map = t2lm_is_valid_t2lm_link_map(vdev, &t2lm_req, &dir);
+	if (valid_map) {
+		mlme_debug("Link match found,accept t2lm conf");
+		status = QDF_STATUS_SUCCESS;
+	} else {
+		status = QDF_STATUS_E_FAILURE;
+		mlme_err("reject t2lm conf");
 	}
 
+	if (QDF_IS_STATUS_SUCCESS(status) &&
+	    t2lm_req.t2lm_info[dir].direction != WLAN_T2LM_INVALID_DIRECTION) {
+		/* Apply T2LM config to peer T2LM ctx and send it to FW */
+		t2lm_info = &ml_peer->t2lm_policy.ongoing_tid_to_link_mapping.t2lm_info[dir];
+		qdf_mem_copy(t2lm_info, &t2lm_req.t2lm_info[dir],
+			     sizeof(struct wlan_t2lm_info));
+		status = wlan_send_tid_to_link_mapping(vdev, t2lm_info);
+		if (!QDF_IS_STATUS_SUCCESS(status))
+			mlme_err("Could not send updated T2LM config to FW");
+	}
+	*token = t2lm_req.dialog_token;
+
+	return status;
+}
+
+QDF_STATUS t2lm_handle_tx_resp(struct wlan_objmgr_vdev *vdev,
+			       void *event_data, uint8_t *token)
+{
 	return QDF_STATUS_SUCCESS;
 }
 
 QDF_STATUS t2lm_handle_tx_req(struct wlan_objmgr_vdev *vdev,
-			      struct t2lm_event_data *event_data)
+			      void *event_data, uint8_t *token)
 {
-	if (!vdev) {
-		mlme_err("vdev is NULL");
-		return QDF_STATUS_E_NULL_VALUE;
-	}
-
 	return QDF_STATUS_SUCCESS;
 }
 
 QDF_STATUS t2lm_handle_rx_resp(struct wlan_objmgr_vdev *vdev,
-			       struct t2lm_event_data *event_data)
+			       void *event_data, uint8_t *token)
 {
-	if (!vdev) {
-		mlme_err("vdev is NULL");
-		return QDF_STATUS_E_NULL_VALUE;
-	}
-
 	return QDF_STATUS_SUCCESS;
 }
 
 QDF_STATUS t2lm_handle_rx_teardown(struct wlan_objmgr_vdev *vdev,
-				   struct t2lm_event_data *event_data)
+				   void *event_data)
 {
-	if (!vdev) {
-		mlme_err("vdev is NULL");
-		return QDF_STATUS_E_NULL_VALUE;
-	}
-
 	return QDF_STATUS_SUCCESS;
 }
 
 QDF_STATUS t2lm_handle_tx_teardown(struct wlan_objmgr_vdev *vdev,
-				   struct t2lm_event_data *event_data)
+				   void *event_data)
 {
-	if (!vdev) {
-		mlme_err("vdev is NULL");
-		return QDF_STATUS_E_NULL_VALUE;
-	}
-
 	return QDF_STATUS_SUCCESS;
 }
 
 QDF_STATUS t2lm_deliver_event(struct wlan_objmgr_vdev *vdev,
+			      struct wlan_objmgr_peer *peer,
 			      enum wlan_t2lm_evt event,
-			      struct t2lm_event_data *event_data)
+			      void *event_data, uint8_t *token)
 {
 	struct wlan_objmgr_psoc *psoc;
 	QDF_STATUS status;
@@ -117,16 +207,16 @@ QDF_STATUS t2lm_deliver_event(struct wlan_objmgr_vdev *vdev,
 
 	switch (event) {
 	case WLAN_T2LM_EV_ACTION_FRAME_RX_REQ:
-		status = t2lm_handle_rx_req(vdev, event_data);
+		status = t2lm_handle_rx_req(vdev, peer, event_data, token);
 		break;
 	case WLAN_T2LM_EV_ACTION_FRAME_TX_RESP:
-		status = t2lm_handle_tx_resp(vdev, event_data);
+		status = t2lm_handle_tx_resp(vdev, event_data, token);
 		break;
 	case WLAN_T2LM_EV_ACTION_FRAME_TX_REQ:
-		status = t2lm_handle_tx_req(vdev, event_data);
+		status = t2lm_handle_tx_req(vdev, event_data, token);
 		break;
 	case WLAN_T2LM_EV_ACTION_FRAME_RX_RESP:
-		status = t2lm_handle_rx_resp(vdev, event_data);
+		status = t2lm_handle_rx_resp(vdev, event_data, token);
 		break;
 	case WLAN_T2LM_EV_ACTION_FRAME_RX_TEARDOWN:
 		status = t2lm_handle_rx_teardown(vdev, event_data);
@@ -250,3 +340,12 @@ wlan_t2lm_validate_candidate(struct cnx_mgr *cm_ctx,
 end:
 	return status;
 }
+
+QDF_STATUS wlan_t2lm_deliver_event(struct wlan_objmgr_vdev *vdev,
+				   struct wlan_objmgr_peer *peer,
+				   enum wlan_t2lm_evt event,
+				   void *event_data,
+				   uint8_t *dialog_token)
+{
+	return t2lm_deliver_event(vdev, peer, event, event_data, dialog_token);
+}

+ 53 - 1
core/mac/src/pe/lim/lim_process_action_frame.c

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-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
@@ -54,6 +54,8 @@
 #include "dot11f.h"
 #include "wlan_p2p_cfg_api.h"
 #include "son_api.h"
+#include "wlan_t2lm_api.h"
+#include "wlan_mlo_mgr_public_structs.h"
 
 #define SA_QUERY_REQ_MIN_LEN \
 (DOT11F_FF_CATEGORY_LEN + DOT11F_FF_ACTION_LEN + DOT11F_FF_TRANSACTIONID_LEN)
@@ -1702,6 +1704,9 @@ void lim_process_action_frame(struct mac_context *mac_ctx,
 	tpSirMacVendorSpecificFrameHdr vendor_specific;
 	uint8_t dpp_oui[] = { 0x50, 0x6F, 0x9A, 0x1A };
 	tpSirMacVendorSpecificPublicActionFrameHdr pub_action;
+	enum wlan_t2lm_resp_frm_type status_code;
+	uint8_t token = 0;
+	struct wlan_objmgr_peer *peer = NULL;
 
 	if (frame_len < sizeof(*action_hdr)) {
 		pe_debug("frame_len %d less than Action Frame Hdr size",
@@ -2136,6 +2141,53 @@ void lim_process_action_frame(struct mac_context *mac_ctx,
 			break;
 		}
 		break;
+	case ACTION_CATEGORY_PROTECTED_EHT:
+		pe_debug("EHT T2LM action category: %d action: %d",
+			 action_hdr->category, action_hdr->actionID);
+		switch (action_hdr->actionID) {
+		case EHT_T2LM_REQUEST:
+			mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info);
+			body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info);
+			frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info);
+
+			peer = wlan_objmgr_get_peer_by_mac(mac_ctx->psoc,
+							   mac_hdr->sa,
+							   WLAN_LEGACY_MAC_ID);
+			if (!peer) {
+				pe_err("Peer is null");
+				break;
+			}
+			if (wlan_t2lm_deliver_event(
+				session->vdev, peer,
+				WLAN_T2LM_EV_ACTION_FRAME_RX_REQ,
+				(void *)body_ptr, &token) == QDF_STATUS_SUCCESS)
+				status_code = WLAN_T2LM_RESP_TYPE_SUCCESS;
+			else
+				status_code =
+				WLAN_T2LM_RESP_TYPE_DENIED_TID_TO_LINK_MAPPING;
+
+			if (lim_send_t2lm_action_rsp_frame(
+					mac_ctx, mac_hdr->sa, session, token,
+					status_code) != QDF_STATUS_SUCCESS)
+				pe_err("T2LM action response frame not sent");
+			break;
+		case EHT_T2LM_RESPONSE:
+			wlan_t2lm_deliver_event(
+					session->vdev, peer,
+					WLAN_T2LM_EV_ACTION_FRAME_RX_RESP,
+					(void *)rx_pkt_info, &token);
+			break;
+		case EHT_T2LM_TEARDOWN:
+			wlan_t2lm_deliver_event(
+					session->vdev, peer,
+					WLAN_T2LM_EV_ACTION_FRAME_RX_TEARDOWN,
+					(void *)rx_pkt_info, NULL);
+			break;
+		default:
+			pe_err("Unhandled T2LM action frame");
+			break;
+		}
+		break;
 	default:
 		pe_warn_rl("Action category: %d not handled",
 			action_hdr->category);

+ 102 - 0
core/mac/src/pe/lim/lim_send_management_frames.c

@@ -6346,6 +6346,108 @@ error_addba_rsp:
 	return qdf_status;
 }
 
+#ifdef WLAN_FEATURE_11BE_MLO
+QDF_STATUS
+lim_send_t2lm_action_rsp_frame(struct mac_context *mac_ctx,
+			       tSirMacAddr peer_mac,
+			       struct pe_session *session, uint8_t token,
+			       enum wlan_t2lm_resp_frm_type status_code)
+{
+	tDot11ft2lm_neg_rsp frm;
+	uint8_t session_id = 0;
+	uint8_t *frame_ptr;
+	tpSirMacMgmtHdr mgmt_hdr;
+	uint32_t num_bytes, payload_size, status;
+	void *pkt_ptr = NULL;
+	QDF_STATUS qdf_status;
+	uint8_t vdev_id = 0;
+	uint8_t tx_flag = 0;
+
+	session_id = session->smeSessionId;
+
+	qdf_mem_zero((uint8_t *)&frm, sizeof(frm));
+	frm.Category.category = ACTION_CATEGORY_PROTECTED_EHT;
+	frm.Action.action = EHT_T2LM_RESPONSE;
+
+	frm.DialogToken.token = token;
+	frm.Status.status = status_code;
+
+	pe_debug("Sending a T2LM negotiation Response from " QDF_MAC_ADDR_FMT " to " QDF_MAC_ADDR_FMT,
+		 QDF_MAC_ADDR_REF(session->self_mac_addr),
+		 QDF_MAC_ADDR_REF(peer_mac));
+	pe_debug("Dialog token %d status %d", frm.DialogToken.token,
+		 frm.Status.status);
+
+	status = dot11f_get_packed_t2lm_neg_rspSize(mac_ctx, &frm,
+						    &payload_size);
+	if (DOT11F_FAILED(status)) {
+		pe_err("Failed to calculate packed size for a T2LM negotiation Response (0x%08x).",
+		       status);
+		/* We'll fall back on the worst case scenario: */
+		payload_size = sizeof(tDot11ft2lm_neg_rsp);
+	} else if (DOT11F_WARNED(status)) {
+		pe_warn("There were warnings while calculating packed size for a T2LM negotiation Response (0x%08x).",
+			status);
+	}
+
+	num_bytes = payload_size + sizeof(*mgmt_hdr);
+	qdf_status = cds_packet_alloc(num_bytes, (void **)&frame_ptr,
+				      (void **)&pkt_ptr);
+	if (!QDF_IS_STATUS_SUCCESS(qdf_status) || (!pkt_ptr)) {
+		pe_err("Failed to allocate %d bytes for a T2LM rsp action frm",
+		       num_bytes);
+		return QDF_STATUS_E_FAILURE;
+	}
+	qdf_mem_zero(frame_ptr, num_bytes);
+
+	lim_populate_mac_header(mac_ctx, frame_ptr, SIR_MAC_MGMT_FRAME,
+				SIR_MAC_MGMT_ACTION, peer_mac,
+				session->self_mac_addr);
+
+	/* Update A3 with the BSSID */
+	mgmt_hdr = (tpSirMacMgmtHdr)frame_ptr;
+	sir_copy_mac_addr(mgmt_hdr->bssId, session->bssId);
+
+	status = dot11f_pack_t2lm_neg_rsp(mac_ctx, &frm,
+					  frame_ptr + sizeof(tSirMacMgmtHdr),
+					  payload_size, &payload_size);
+
+	if (DOT11F_FAILED(status)) {
+		pe_err("Failed to pack a T2LM negotiation response (0x%08x)",
+		       status);
+		qdf_status = QDF_STATUS_E_FAILURE;
+		goto error_addba_rsp;
+	} else if (DOT11F_WARNED(status)) {
+		pe_warn("There were warnings while packing T2LM rsp (0x%08x)",
+			status);
+	}
+
+	if (!wlan_reg_is_24ghz_ch_freq(session->curr_op_freq) ||
+	    session->opmode == QDF_P2P_CLIENT_MODE ||
+	    session->opmode == QDF_P2P_GO_MODE)
+		tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME;
+
+	MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT,
+			 session->peSessionId, mgmt_hdr->fc.subType));
+	qdf_status = wma_tx_frame(mac_ctx, pkt_ptr, (uint16_t)num_bytes,
+				  TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, 7,
+				  lim_tx_complete, frame_ptr, tx_flag,
+				  vdev_id, 0, RATEID_DEFAULT, 0);
+	MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE,
+			 session->peSessionId, qdf_status));
+	if (QDF_STATUS_SUCCESS != qdf_status) {
+		pe_err("wma_tx_frame FAILED! Status [%d]", qdf_status);
+		return QDF_STATUS_E_FAILURE;
+	} else {
+		return QDF_STATUS_SUCCESS;
+	}
+
+error_addba_rsp:
+	cds_packet_free((void *)pkt_ptr);
+	return qdf_status;
+}
+#endif
+
 /**
  * lim_delba_tx_complete_cnf() - Confirmation for Delba OTA
  * @context: pointer to global mac

+ 28 - 0
core/mac/src/pe/lim/lim_types.h

@@ -1414,6 +1414,34 @@ QDF_STATUS lim_send_delba_action_frame(struct mac_context *mac_ctx,
 				       uint8_t *peer_macaddr, uint8_t tid,
 				       uint8_t reason_code);
 
+#ifdef WLAN_FEATURE_11BE_MLO
+/**
+ * lim_send_t2lm_action_rsp_frame() - Send T2LM negotiation response to peer
+ * @mac_ctx: mac context
+ * @peer_mac: Peer mac addr
+ * @session: PE session entry
+ * @token: Dialog token
+ * @status_code: T2LM negotiation response status code
+ *
+ * Return: 0 for success, non-zero for failure
+ */
+QDF_STATUS
+lim_send_t2lm_action_rsp_frame(struct mac_context *mac_ctx,
+			       tSirMacAddr peer_mac,
+			       struct pe_session *session,
+			       uint8_t token,
+			       enum wlan_t2lm_resp_frm_type status_code);
+#else
+static inline QDF_STATUS
+lim_send_t2lm_action_rsp_frame(struct mac_context *mac_ctx,
+			       tSirMacAddr peer_mac,
+			       struct pe_session *session, uint8_t token,
+			       enum wlan_t2lm_resp_frm_type status_code)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif
+
 /**
  * lim_process_join_failure_timeout() - This function is called to process
  * JoinFailureTimeout