浏览代码

qcacmn: Add support for T2LM timer handling

Adds api's to handle t2lm timer functionality.
Api's added are timer initialize, start, stop, expiry handler.

Change-Id: If52453135921067d04f8349ece64c33cd524af00
CRs-Fixed: 3342822
Amruta Kulkarni 2 年之前
父节点
当前提交
8759cf1c1e

+ 90 - 0
target_if/mlo_mgr/src/target_if_mlo_mgr.c

@@ -23,6 +23,8 @@
 #include <wmi_unified_11be_api.h>
 #include <init_deinit_lmac.h>
 #include "target_if_mlo_mgr.h"
+#include <wlan_objmgr_peer_obj.h>
+#include <wlan_mlo_t2lm.h>
 
 /**
  * target_if_mlo_link_set_active_resp_handler() - function to handle mlo link
@@ -238,6 +240,92 @@ target_if_mlo_unregister_vdev_tid_to_link_map_event(
 			wmi_handle, wmi_mlo_ap_vdev_tid_to_link_map_eventid);
 }
 
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+/**
+ * target_if_fill_provisioned_links() - API to fill the provisioned links
+ * @params: Pointer to T2LM params structure
+ * @t2lm: Pointer to T2LM info structure
+ *
+ * Return: none
+ */
+static inline void target_if_fill_provisioned_links(
+		struct wmi_host_tid_to_link_map_params *params,
+		struct wlan_t2lm_info *t2lm)
+{
+	qdf_mem_copy(&params->t2lm_info[params->num_dir].t2lm_provisioned_links,
+		     &t2lm->ieee_link_map_tid,
+		     sizeof(uint16_t) * T2LM_MAX_NUM_TIDS);
+}
+#else
+static inline void target_if_fill_provisioned_links(
+		struct wmi_host_tid_to_link_map_params *params,
+		struct wlan_t2lm_info *t2lm)
+{
+	qdf_mem_copy(&params->t2lm_info[params->num_dir].t2lm_provisioned_links,
+		     &t2lm->hw_link_map_tid,
+		     sizeof(uint16_t) * T2LM_MAX_NUM_TIDS);
+}
+#endif
+
+static QDF_STATUS
+target_if_mlo_send_tid_to_link_mapping(struct wlan_objmgr_vdev *vdev,
+				       struct wlan_t2lm_info *t2lm)
+{
+	struct wmi_unified *wmi_handle = NULL;
+	struct wmi_host_tid_to_link_map_params params = {0};
+	struct wlan_objmgr_pdev *pdev = NULL;
+	int tid = 0;
+	QDF_STATUS status;
+
+	pdev = wlan_vdev_get_pdev(vdev);
+	if (!pdev) {
+		t2lm_err("null pdev");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	wmi_handle = lmac_get_pdev_wmi_handle(pdev);
+	if (!wmi_handle) {
+		t2lm_err("null wmi handle");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	params.pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
+	qdf_mem_copy(params.peer_macaddr, vdev->vdev_objmgr.bss_peer->macaddr,
+		     QDF_MAC_ADDR_SIZE);
+
+	t2lm_debug("Fill T2LM WMI info for peer: " QDF_MAC_ADDR_FMT " pdev_id:%d",
+		   QDF_MAC_ADDR_REF(params.peer_macaddr), params.pdev_id);
+
+	params.t2lm_info[params.num_dir].direction = t2lm->direction;
+	params.t2lm_info[params.num_dir].default_link_mapping =
+		t2lm->default_link_mapping;
+
+	if (!params.t2lm_info[params.num_dir].default_link_mapping)
+		target_if_fill_provisioned_links(&params, t2lm);
+
+	t2lm_debug("num_dir:%d direction:%d default_link_mapping:%d",
+		   params.num_dir, params.t2lm_info[params.num_dir].direction,
+		   params.t2lm_info[params.num_dir].default_link_mapping);
+
+	for (tid = 0; tid < T2LM_MAX_NUM_TIDS; tid++) {
+		t2lm_debug("tid:%d hw_link_map:%x ieee_lin_map:%x", tid,
+			   params.t2lm_info[params.num_dir].t2lm_provisioned_links[tid],
+			   t2lm->ieee_link_map_tid[tid]);
+	}
+
+	params.num_dir++;
+
+	status = wmi_send_mlo_peer_tid_to_link_map_cmd(wmi_handle, &params);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		t2lm_err("Failed to send T2LM WMI command for pdev_id:%d peer_mac: " QDF_MAC_ADDR_FMT,
+			 params.pdev_id,
+			 QDF_MAC_ADDR_REF(params.peer_macaddr));
+		return status;
+	}
+
+	return status;
+}
+
 /**
  * target_if_mlo_register_tx_ops() - lmac handler to register mlo tx ops
  *  callback functions
@@ -266,6 +354,8 @@ target_if_mlo_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops)
 	mlo_tx_ops->unregister_events =
 		target_if_mlo_unregister_event_handler;
 	mlo_tx_ops->link_set_active = target_if_mlo_link_set_active;
+	mlo_tx_ops->send_tid_to_link_mapping =
+		target_if_mlo_send_tid_to_link_mapping;
 
 	return QDF_STATUS_SUCCESS;
 }

+ 3 - 0
umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h

@@ -1387,6 +1387,7 @@ struct wlan_lmac_if_son_rx_ops {
  * @register_events: function to register event handlers with FW
  * @unregister_events: function to de-register event handlers with FW
  * @link_set_active: function to send mlo link set active command to FW
+ * @send_tid_to_link_mapping: function to send T2LM command to FW
  */
 struct wlan_lmac_if_mlo_tx_ops {
 	QDF_STATUS (*register_events)(struct wlan_objmgr_psoc *psoc);
@@ -1396,6 +1397,8 @@ struct wlan_lmac_if_mlo_tx_ops {
 #ifdef WLAN_MLO_GLOBAL_SHMEM_SUPPORT
 	struct wlan_lmac_if_global_shmem_local_ops shmem_local_ops;
 #endif
+	QDF_STATUS (*send_tid_to_link_mapping)(struct wlan_objmgr_vdev *vdev,
+					       struct wlan_t2lm_info *t2lm);
 };
 
 /**

+ 14 - 1
umac/mlo_mgr/inc/wlan_mlo_mgr_public_structs.h

@@ -300,6 +300,7 @@ enum wlan_t2lm_direction {
 	WLAN_T2LM_INVALID_DIRECTION,
 };
 
+#define T2LM_EXPECTED_DURATION_MAX_VALUE 0xFFFFFF
 /**
  * struct wlan_t2lm_info - TID-to-Link mapping information for the frames
  * transmitted on the uplink, downlink and bidirectional.
@@ -352,11 +353,21 @@ struct wlan_mlo_t2lm_ie {
  * struct wlan_t2lm_timer - T2LM timer information
  *
  * @t2lm_timer: T2LM timer
- * @timer_interval: T2LM timer interval value
+ * @timer_interval: T2LM Timer value
+ * @timer_started: T2LM timer started or not
+ * @t2lm_ie_index: T2LM IE index value
+ * @t2lm_dev_lock: lock to access struct
  */
 struct wlan_t2lm_timer {
 	qdf_timer_t t2lm_timer;
 	uint32_t timer_interval;
+	bool timer_started;
+	uint8_t t2lm_ie_index;
+#ifdef WLAN_MLO_USE_SPINLOCK
+	qdf_spinlock_t t2lm_dev_lock;
+#else
+	qdf_mutex_t t2lm_dev_lock;
+#endif
 };
 
 /**
@@ -366,6 +377,7 @@ struct wlan_t2lm_timer {
  * @t2lm_ie: T2LM IE information
  * @t2lm_timer: T2LM timer information
  * @t2lm_dev_lock: t2lm dev context lock
+ * @tsf: time sync func value received via beacon
  */
 struct wlan_t2lm_context {
 	uint8_t num_of_t2lm_ie;
@@ -376,6 +388,7 @@ struct wlan_t2lm_context {
 #else
 	qdf_mutex_t t2lm_dev_lock;
 #endif
+	uint64_t tsf;
 };
 
 /*

+ 180 - 0
umac/mlo_mgr/inc/wlan_mlo_t2lm.h

@@ -45,6 +45,86 @@
 
 #define WLAN_T2LM_MAX_NUM_LINKS 16
 
+#ifdef WLAN_MLO_USE_SPINLOCK
+/**
+ * t2lm_dev_lock_create - Create T2LM device mutex/spinlock
+ * @t2lm_ctx: T2LM context
+ *
+ * Creates mutex/spinlock
+ *
+ * Return: void
+ */
+static inline void
+t2lm_dev_lock_create(struct wlan_t2lm_context *t2lm_ctx)
+{
+	qdf_spinlock_create(&t2lm_ctx->t2lm_dev_lock);
+}
+
+/**
+ * t2lm_dev_lock_destroy - Destroy T2LM mutex/spinlock
+ * @t2lm_ctx: T2LM context
+ *
+ * Destroy mutex/spinlock
+ *
+ * Return: void
+ */
+static inline void
+t2lm_dev_lock_destroy(struct wlan_t2lm_context *t2lm_ctx)
+{
+	qdf_spinlock_destroy(&t2lm_ctx->t2lm_dev_lock);
+}
+
+/**
+ * t2lm_dev_lock_acquire - acquire T2LM mutex/spinlock
+ * @t2lm_ctx: T2LM context
+ *
+ * acquire mutex/spinlock
+ *
+ * return: void
+ */
+static inline
+void t2lm_dev_lock_acquire(struct wlan_t2lm_context *t2lm_ctx)
+{
+	qdf_spin_lock_bh(&t2lm_ctx->t2lm_dev_lock);
+}
+
+/**
+ * t2lm_dev_lock_release - release T2LM dev mutex/spinlock
+ * @t2lm_ctx: T2LM context
+ *
+ * release mutex/spinlock
+ *
+ * return: void
+ */
+static inline
+void t2lm_dev_lock_release(struct wlan_t2lm_context *t2lm_ctx)
+{
+	qdf_spin_unlock_bh(&t2lm_ctx->t2lm_dev_lock);
+}
+#else /* WLAN_MLO_USE_SPINLOCK */
+static inline
+void t2lm_dev_lock_create(struct wlan_t2lm_context *t2lm_ctx)
+{
+	qdf_mutex_create(&t2lm_ctx->t2lm_dev_lock);
+}
+
+static inline
+void t2lm_dev_lock_destroy(struct wlan_t2lm_context *t2lm_ctx)
+{
+	qdf_mutex_destroy(&t2lm_ctx->t2lm_dev_lock);
+}
+
+static inline void t2lm_dev_lock_acquire(struct wlan_t2lm_context *t2lm_ctx)
+{
+	qdf_mutex_acquire(&t2lm_ctx->t2lm_dev_lock);
+}
+
+static inline void t2lm_dev_lock_release(struct wlan_t2lm_context *t2lm_ctx)
+{
+	qdf_mutex_release(&t2lm_ctx->t2lm_dev_lock);
+}
+#endif
+
 /**
  * wlan_mlo_parse_t2lm_ie() - API to parse the T2LM IE
  * @t2lm: Pointer to T2LM structure
@@ -122,6 +202,69 @@ QDF_STATUS wlan_mlo_parse_bcn_prbresp_t2lm_ie(
  * Return: Updated frame pointer
  */
 uint8_t *wlan_mlo_add_t2lm_info_ie(uint8_t *frm, struct wlan_t2lm_info *t2lm);
+
+/**
+ * wlan_mlo_t2lm_timer_init() - API to add TID-to-link mapping IE
+ * @vdev: Pointer to vdev
+ *
+ * Return: qdf status
+ */
+QDF_STATUS
+wlan_mlo_t2lm_timer_init(struct wlan_objmgr_vdev *vdev);
+
+/**
+ * wlan_mlo_t2lm_timer_start() - API to start T2LM timer
+ * @vdev: Pointer to vdev
+ * @interval: T2LM timer interval
+ * @t2lm_ie_index: T2LM IE index
+ *
+ * Return: qdf status
+ */
+QDF_STATUS
+wlan_mlo_t2lm_timer_start(struct wlan_objmgr_vdev *vdev,
+			  uint32_t interval, uint8_t t2lm_ie_index);
+
+/**
+ * wlan_mlo_t2lm_timer_stop() - API to stop TID-to-link mapping timer
+ * @vdev: Pointer to vdev
+ *
+ * Return: qdf status
+ */
+QDF_STATUS
+wlan_mlo_t2lm_timer_stop(struct wlan_objmgr_vdev *vdev);
+
+/**
+ * wlan_mlo_t2lm_timer_expiry_handler() - API to handle t2lm timer expiry
+ * @vdev: Pointer to vdev structure
+ *
+ * Return: none
+ */
+void
+wlan_mlo_t2lm_timer_expiry_handler(void *vdev);
+
+/**
+ * wlan_handle_t2lm_timer() - API to handle TID-to-link mapping timer
+ * @vdev: Pointer to vdev
+ * @ie_idx: ie index value
+ *
+ * Return: qdf status
+ */
+QDF_STATUS
+wlan_handle_t2lm_timer(struct wlan_objmgr_vdev *vdev, uint8_t ie_idx);
+
+/**
+ * wlan_process_bcn_prbrsp_t2lm_ie() - API to process the received T2LM IE from
+ * beacon/probe response.
+ * @vdev: Pointer to vdev
+ * @rx_t2lm_ie: Received T2LM IE
+ * @tsf: Local TSF value
+ *
+ * Return QDF_STATUS
+ */
+QDF_STATUS wlan_process_bcn_prbrsp_t2lm_ie(struct wlan_objmgr_vdev *vdev,
+					   struct wlan_t2lm_context *rx_t2lm_ie,
+					   uint64_t tsf);
+
 #else
 static inline QDF_STATUS wlan_mlo_parse_t2lm_ie(
 	struct wlan_t2lm_onging_negotiation_info *t2lm, uint8_t *ie)
@@ -165,5 +308,42 @@ uint8_t *wlan_mlo_add_t2lm_info_ie(uint8_t *frm, struct wlan_t2lm_info *t2lm)
 {
 	return frm;
 }
+
+static inline QDF_STATUS
+wlan_mlo_t2lm_timer_init(struct wlan_objmgr_vdev *vdev)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
+static inline QDF_STATUS
+wlan_mlo_t2lm_timer_start(struct wlan_objmgr_vdev *vdev,
+			  uint32_t interval, uint8_t t2lm_ie_index)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
+static inline QDF_STATUS
+wlan_mlo_t2lm_timer_stop(struct wlan_objmgr_vdev *vdev)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
+static inline void
+wlan_mlo_t2lm_timer_expiry_handler(void *vdev)
+{}
+
+static inline QDF_STATUS
+wlan_handle_t2lm_timer(struct wlan_objmgr_vdev *vdev, uint8_t ie_idx)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
+static inline QDF_STATUS
+wlan_process_bcn_prbrsp_t2lm_ie(struct wlan_objmgr_vdev *vdev,
+				struct wlan_t2lm_context *rx_t2lm_ie,
+				uint64_t tsf)
+{
+	return QDF_STATUS_SUCCESS;
+}
 #endif /* WLAN_FEATURE_11BE */
 #endif /* _WLAN_MLO_T2LM_H_ */

+ 7 - 2
umac/mlo_mgr/src/wlan_mlo_mgr_main.c

@@ -29,6 +29,7 @@
 #include <wlan_cm_public_struct.h>
 #include "wlan_mlo_mgr_msgq.h"
 #include <target_if_mlo_mgr.h>
+#include <wlan_mlo_t2lm.h>
 
 static void mlo_global_ctx_deinit(void)
 {
@@ -436,10 +437,12 @@ QDF_STATUS wlan_mlo_check_valid_config(struct wlan_mlo_dev_context *ml_dev,
  * mlo_t2lm_ctx_init() - API to initialize the t2lm context with the default
  * values.
  * @ml_dev: Pointer to ML Dev context
+ * @vdev: Pointer to vdev structure
  *
  * Return: None
  */
-static inline void mlo_t2lm_ctx_init(struct wlan_mlo_dev_context *ml_dev)
+static inline void mlo_t2lm_ctx_init(struct wlan_mlo_dev_context *ml_dev,
+				     struct wlan_objmgr_vdev *vdev)
 {
 	struct wlan_t2lm_info *t2lm;
 
@@ -450,6 +453,8 @@ static inline void mlo_t2lm_ctx_init(struct wlan_mlo_dev_context *ml_dev)
 	ml_dev->t2lm_ctx.num_of_t2lm_ie = 1;
 	t2lm->direction = WLAN_T2LM_BIDI_DIRECTION;
 	t2lm->default_link_mapping = 1;
+
+	wlan_mlo_t2lm_timer_init(vdev);
 }
 
 static QDF_STATUS mlo_dev_ctx_init(struct wlan_objmgr_vdev *vdev)
@@ -527,7 +532,7 @@ static QDF_STATUS mlo_dev_ctx_init(struct wlan_objmgr_vdev *vdev)
 		qdf_list_insert_back(&g_mlo_ctx->ml_dev_list, &ml_dev->node);
 	ml_link_lock_release(g_mlo_ctx);
 
-	mlo_t2lm_ctx_init(ml_dev);
+	mlo_t2lm_ctx_init(ml_dev, vdev);
 
 	return status;
 }

+ 334 - 6
umac/mlo_mgr/src/wlan_mlo_t2lm.c

@@ -25,6 +25,7 @@
 #include <wlan_mlo_t2lm.h>
 #include <wlan_mlo_mgr_cmn.h>
 #include <qdf_util.h>
+#include <wlan_cm_api.h>
 
 /**
  * wlan_mlo_parse_t2lm_info() - Parse T2LM IE fields
@@ -621,12 +622,31 @@ static void wlan_mlo_t2lm_handle_expected_duration_expiry(
 		if (!t2lm_ctx->t2lm_ie[i].t2lm.expected_duration_present)
 			continue;
 
-		qdf_mem_zero(&t2lm_ctx->t2lm_ie[i],
-			     sizeof(struct wlan_mlo_t2lm_ie));
-		t2lm_ctx->t2lm_ie[i].t2lm.direction = WLAN_T2LM_BIDI_DIRECTION;
-		t2lm_ctx->t2lm_ie[i].t2lm.default_link_mapping = 1;
-		t2lm_debug("vdev_id:%d Expected duration is expired",
-			   vdev_id);
+		/* If two T2LM IEs are present, and expected duration of first
+		 * T2LM IE is expired, copy the T2LM IE from index 1 to index 0.
+		 * Mark mapping switch time present as false and clear the
+		 * mapping switch time value.
+		 * If one T2LM IE is present, and the expected duration is
+		 * expired, configure the T2LM IE with the default values.
+		 */
+		if (!i && t2lm_ctx->num_of_t2lm_ie == WLAN_MAX_T2LM_IE) {
+			qdf_mem_copy(&t2lm_ctx->t2lm_ie[0],
+				     &t2lm_ctx->t2lm_ie[1],
+				     sizeof(struct wlan_mlo_t2lm_ie));
+			t2lm_ctx->t2lm_ie[0].t2lm.mapping_switch_time_present =
+				false;
+			t2lm_ctx->t2lm_ie[0].t2lm.mapping_switch_time = 0;
+			t2lm_debug("vdev_id:%d mark the advertised T2LM as established",
+				   vdev_id);
+		} else {
+			qdf_mem_zero(&t2lm_ctx->t2lm_ie[i],
+				     sizeof(struct wlan_mlo_t2lm_ie));
+			t2lm_ctx->t2lm_ie[i].t2lm.direction =
+				WLAN_T2LM_BIDI_DIRECTION;
+			t2lm_ctx->t2lm_ie[i].t2lm.default_link_mapping = 1;
+			t2lm_debug("vdev_id:%d Expected duration is expired",
+				   vdev_id);
+		}
 	}
 }
 
@@ -678,3 +698,311 @@ QDF_STATUS wlan_mlo_vdev_tid_to_link_map_event(
 
 	return QDF_STATUS_SUCCESS;
 }
+
+static
+QDF_STATUS wlan_send_tid_to_link_mapping(struct wlan_objmgr_vdev *vdev,
+					 struct wlan_t2lm_info *t2lm)
+{
+	struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops;
+	struct wlan_objmgr_vdev *co_mld_vdev;
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = {NULL};
+	uint16_t vdev_count = 0;
+	int i = 0;
+	QDF_STATUS status = QDF_STATUS_E_FAILURE;
+
+	psoc = wlan_vdev_get_psoc(vdev);
+	if (!psoc) {
+		t2lm_err("null psoc");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	mlo_tx_ops = &psoc->soc_cb.tx_ops->mlo_ops;
+	if (!mlo_tx_ops) {
+		t2lm_err("tx_ops is null!");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!mlo_tx_ops->send_tid_to_link_mapping) {
+		t2lm_err("send_tid_to_link_mapping is null");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	mlo_get_ml_vdev_list(vdev, &vdev_count, wlan_vdev_list);
+	if (!vdev_count) {
+		t2lm_err("Number of VDEVs under MLD is reported as 0");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	for (i = 0; i < vdev_count; i++) {
+		co_mld_vdev = wlan_vdev_list[i];
+		if (!co_mld_vdev) {
+			t2lm_err("co_mld_vdev is null");
+			mlo_release_vdev_ref(co_mld_vdev);
+			continue;
+		}
+
+		status = mlo_tx_ops->send_tid_to_link_mapping(co_mld_vdev,
+							      t2lm);
+		if (QDF_IS_STATUS_ERROR(status))
+			t2lm_err("Failed to send T2LM command to FW");
+		mlo_release_vdev_ref(co_mld_vdev);
+	}
+
+	return status;
+}
+
+void wlan_mlo_t2lm_timer_expiry_handler(void *vdev)
+{
+	struct wlan_objmgr_vdev *vdev_ctx = (struct wlan_objmgr_vdev *)vdev;
+
+	struct wlan_t2lm_timer *t2lm_timer;
+	struct wlan_t2lm_context *t2lm_ctx;
+	uint8_t t2lm_ie_idx;
+
+	if (!vdev_ctx || !vdev_ctx->mlo_dev_ctx)
+		return;
+
+	t2lm_ctx = &vdev_ctx->mlo_dev_ctx->t2lm_ctx;
+	t2lm_timer = &vdev_ctx->mlo_dev_ctx->t2lm_ctx.t2lm_timer;
+	t2lm_ie_idx = t2lm_timer->t2lm_ie_index;
+	if (t2lm_ie_idx >= WLAN_MAX_T2LM_IE)
+		return;
+
+	wlan_mlo_t2lm_timer_stop(vdev_ctx);
+
+	if (t2lm_ctx->t2lm_ie[t2lm_ie_idx].t2lm.mapping_switch_time_present) {
+		wlan_send_tid_to_link_mapping(
+				vdev, &t2lm_ctx->t2lm_ie[t2lm_ie_idx].t2lm);
+		wlan_mlo_t2lm_handle_mapping_switch_time_expiry(t2lm_ctx, vdev);
+		wlan_handle_t2lm_timer(vdev_ctx, t2lm_timer->t2lm_ie_index);
+	} else if (!t2lm_ie_idx) {
+		wlan_mlo_t2lm_handle_expected_duration_expiry(t2lm_ctx, vdev);
+		wlan_handle_t2lm_timer(vdev_ctx, t2lm_timer->t2lm_ie_index);
+	}
+}
+
+QDF_STATUS
+wlan_mlo_t2lm_timer_init(struct wlan_objmgr_vdev *vdev)
+{
+	struct wlan_t2lm_timer *t2lm_timer = NULL;
+
+	if (!vdev || !vdev->mlo_dev_ctx)
+		return QDF_STATUS_E_FAILURE;
+
+	t2lm_timer = &vdev->mlo_dev_ctx->t2lm_ctx.t2lm_timer;
+	if (!t2lm_timer) {
+		t2lm_err("t2lm timer ctx is null");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	t2lm_dev_lock_acquire(&vdev->mlo_dev_ctx->t2lm_ctx);
+	qdf_timer_init(NULL, &t2lm_timer->t2lm_timer,
+		       wlan_mlo_t2lm_timer_expiry_handler,
+		       vdev, QDF_TIMER_TYPE_WAKE_APPS);
+
+	t2lm_timer->timer_started = false;
+	t2lm_timer->timer_interval = 0;
+	t2lm_timer->t2lm_ie_index = 0;
+	t2lm_dev_lock_release(&vdev->mlo_dev_ctx->t2lm_ctx);
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS
+wlan_mlo_t2lm_timer_start(struct wlan_objmgr_vdev *vdev,
+			  uint32_t interval, uint8_t t2lm_ie_index)
+{
+	struct wlan_t2lm_timer *t2lm_timer;
+	struct wlan_t2lm_context *t2lm_ctx;
+	struct vdev_mlme_obj *vdev_mlme;
+
+	if (!vdev || !vdev->mlo_dev_ctx)
+		return QDF_STATUS_E_NULL_VALUE;
+
+	if (interval == 0) {
+		t2lm_debug("Timer interval is 0");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(vdev);
+	if (!vdev_mlme)
+		return QDF_STATUS_E_FAILURE;
+
+	t2lm_ctx = &vdev->mlo_dev_ctx->t2lm_ctx;
+	t2lm_timer = &vdev->mlo_dev_ctx->t2lm_ctx.t2lm_timer;
+	if (!t2lm_timer) {
+		t2lm_err("t2lm timer ctx is null");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	t2lm_debug("t2lm timer started with interval %d", interval);
+	t2lm_dev_lock_acquire(&vdev->mlo_dev_ctx->t2lm_ctx);
+	if (t2lm_ctx->t2lm_ie[t2lm_ie_index].t2lm.mapping_switch_time_present)
+		t2lm_timer->timer_interval =
+			t2lm_ctx->t2lm_ie[t2lm_ie_index].t2lm.mapping_switch_time;
+	else
+		t2lm_timer->timer_interval = interval *
+			vdev_mlme->proto.generic.beacon_interval * 1000;
+
+	t2lm_timer->t2lm_ie_index = t2lm_ie_index;
+	t2lm_timer->timer_started = true;
+	qdf_timer_start(&t2lm_timer->t2lm_timer, t2lm_timer->timer_interval);
+	t2lm_dev_lock_release(&vdev->mlo_dev_ctx->t2lm_ctx);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS
+wlan_mlo_t2lm_timer_stop(struct wlan_objmgr_vdev *vdev)
+{
+	struct wlan_t2lm_timer *t2lm_timer;
+
+	if (!vdev || !vdev->mlo_dev_ctx)
+		return QDF_STATUS_E_NULL_VALUE;
+
+	t2lm_timer = &vdev->mlo_dev_ctx->t2lm_ctx.t2lm_timer;
+	if (!t2lm_timer) {
+		t2lm_err("t2lm timer ctx is null");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	t2lm_dev_lock_acquire(&vdev->mlo_dev_ctx->t2lm_ctx);
+	if (t2lm_timer->timer_started) {
+		qdf_timer_stop(&t2lm_timer->t2lm_timer);
+		t2lm_timer->timer_started = false;
+		t2lm_timer->timer_interval = 0;
+	}
+	t2lm_dev_lock_release(&vdev->mlo_dev_ctx->t2lm_ctx);
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS wlan_handle_t2lm_timer(struct wlan_objmgr_vdev *vdev, uint8_t ie_idx)
+{
+	struct wlan_t2lm_context *t2lm_ctx;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	if (!vdev || !vdev->mlo_dev_ctx)
+		return QDF_STATUS_E_NULL_VALUE;
+
+	t2lm_ctx = &vdev->mlo_dev_ctx->t2lm_ctx;
+	if (!t2lm_ctx->num_of_t2lm_ie) {
+		t2lm_err("No T2LM IE present");
+		return QDF_STATUS_SUCCESS;
+	}
+
+	if (ie_idx >= WLAN_MAX_T2LM_IE) {
+		t2lm_err("Invalid T2LM IE index");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (!t2lm_ctx->t2lm_ie[ie_idx].t2lm.mapping_switch_time_present &&
+	    !t2lm_ctx->t2lm_ie[ie_idx].t2lm.expected_duration_present) {
+		/* non-default to default mapping case */
+		wlan_send_tid_to_link_mapping(vdev,
+					      &t2lm_ctx->t2lm_ie[ie_idx].t2lm);
+	} else if (t2lm_ctx->t2lm_ie[ie_idx].t2lm.mapping_switch_time_present) {
+		/* Default to non-default mapping case */
+		status = wlan_mlo_t2lm_timer_start(
+				vdev,
+				t2lm_ctx->t2lm_ie[ie_idx].t2lm.mapping_switch_time,
+				ie_idx);
+	} else if (t2lm_ctx->t2lm_ie[ie_idx].t2lm.expected_duration_present) {
+		wlan_send_tid_to_link_mapping(
+				vdev, &t2lm_ctx->t2lm_ie[ie_idx].t2lm);
+
+		if (t2lm_ctx->t2lm_ie[ie_idx].t2lm.expected_duration !=
+		    T2LM_EXPECTED_DURATION_MAX_VALUE)
+			status = wlan_mlo_t2lm_timer_start(
+					vdev,
+					t2lm_ctx->t2lm_ie[ie_idx].t2lm.expected_duration,
+					ie_idx);
+	}
+
+	return status;
+}
+
+/**
+ * wlan_update_mapping_switch_time_expected_dur() - API to update the mapping
+ * switch time and expected duration.
+ * @vdev:Pointer to vdev
+ * @rx_t2lm: Pointer to received T2LM IE
+ * @tsf: TSF value of beacon/probe response
+ *
+ * Return: None
+ */
+static void wlan_update_mapping_switch_time_expected_dur(
+		struct wlan_objmgr_vdev *vdev, struct wlan_t2lm_info *rx_t2lm,
+		uint64_t tsf)
+{
+	struct wlan_t2lm_context *t2lm_ctx;
+	uint16_t tsf_bit25_16, ms_time;
+	bool match_found = false;
+	int j;
+
+	tsf_bit25_16 = (tsf & 0x3FF0000) >> 16;
+	t2lm_ctx = &vdev->mlo_dev_ctx->t2lm_ctx;
+
+	for (j = 0; j < t2lm_ctx->num_of_t2lm_ie; j++) {
+		/* Match not found */
+		if (qdf_mem_cmp(rx_t2lm->ieee_link_map_tid,
+				t2lm_ctx->t2lm_ie[j].t2lm.ieee_link_map_tid,
+				sizeof(uint16_t) * T2LM_MAX_NUM_TIDS))
+			continue;
+
+		if (t2lm_ctx->t2lm_ie[j].t2lm.mapping_switch_time_present) {
+			ms_time = rx_t2lm->mapping_switch_time;
+
+			if (ms_time > tsf_bit25_16) {
+				t2lm_ctx->t2lm_ie[j].t2lm.mapping_switch_time =
+					((ms_time - tsf_bit25_16) * 1024) / 1000;
+			} else {
+				t2lm_ctx->t2lm_ie[j].t2lm.mapping_switch_time =
+					((0xFFFF - (tsf_bit25_16 - ms_time)) * 1024) / 1000;
+			}
+		}
+
+		if (t2lm_ctx->t2lm_ie[j].t2lm.expected_duration_present) {
+			t2lm_ctx->t2lm_ie[j].t2lm.expected_duration =
+				rx_t2lm->expected_duration;
+		}
+
+		match_found = true;
+		break;
+	}
+
+	if (!match_found &&
+	    t2lm_ctx->num_of_t2lm_ie < WLAN_MAX_T2LM_IE) {
+		qdf_mem_copy(&t2lm_ctx->t2lm_ie[t2lm_ctx->num_of_t2lm_ie].t2lm,
+			     rx_t2lm, sizeof(struct wlan_t2lm_info));
+		t2lm_ctx->num_of_t2lm_ie++;
+	}
+}
+
+QDF_STATUS wlan_process_bcn_prbrsp_t2lm_ie(
+		struct wlan_objmgr_vdev *vdev,
+		struct wlan_t2lm_context *rx_t2lm_ie, uint64_t tsf)
+{
+	struct wlan_t2lm_context *t2lm_ctx;
+	int i;
+
+	t2lm_ctx = &vdev->mlo_dev_ctx->t2lm_ctx;
+
+	for (i = 0; i < rx_t2lm_ie->num_of_t2lm_ie; i++) {
+		wlan_update_mapping_switch_time_expected_dur(
+				vdev, &rx_t2lm_ie->t2lm_ie[i].t2lm, tsf);
+	}
+
+	if (!wlan_cm_is_vdev_connected(vdev))
+		return QDF_STATUS_SUCCESS;
+
+	/* Do not start the timer if STA is not in connected state */
+	for (i = 0; i < t2lm_ctx->num_of_t2lm_ie; i++) {
+		if (t2lm_ctx->t2lm_ie[i].t2lm.mapping_switch_time_present ||
+		    t2lm_ctx->t2lm_ie[i].t2lm.expected_duration_present) {
+			wlan_handle_t2lm_timer(vdev, i);
+			break;
+		}
+	}
+
+	return QDF_STATUS_SUCCESS;
+}