Browse Source

qcacld-3.0: MLO roaming handling

CM, PE and WMA changes for mlo roaming
- Roam start and roam sync propogation updates.
- Handling connected link bitmap.
- Link specific assoc response generation
- WMA changes to handle add/remove peer
for mlo roaming scenarios.

Change-Id: I365a26ebb761d93dadd33c9fb8248c28e9eda94b
CRs-Fixed: 3033766
Amruta Kulkarni 3 years ago
parent
commit
586338bcff

+ 61 - 25
components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_fw_sync.c

@@ -45,6 +45,7 @@
 #include "connection_mgr/core/src/wlan_cm_main.h"
 #include "connection_mgr/core/src/wlan_cm_sm.h"
 #include <wlan_mlo_mgr_sta.h>
+#include "wlan_mlo_mgr_roam.h"
 
 QDF_STATUS cm_fw_roam_sync_req(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id,
 			       void *event, uint32_t event_data_len)
@@ -127,15 +128,32 @@ error:
 
 QDF_STATUS
 cm_fw_roam_sync_start_ind(struct wlan_objmgr_vdev *vdev,
-			  uint8_t roam_reason)
+			  struct roam_offload_synch_ind *sync_ind)
 {
-	QDF_STATUS status;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
 	struct wlan_objmgr_pdev *pdev;
 	struct qdf_mac_addr connected_bssid;
 	uint8_t vdev_id;
+	struct wlan_objmgr_psoc *psoc;
 
 	pdev = wlan_vdev_get_pdev(vdev);
 	vdev_id = wlan_vdev_get_id(vdev);
+	psoc = wlan_pdev_get_psoc(pdev);
+
+	if (wlan_vdev_mlme_is_mlo_link_vdev(vdev)) {
+		if (!MLME_IS_ROAM_SYNCH_IN_PROGRESS(psoc,
+					sync_ind->roamed_vdev_id))
+			status = wlan_cm_roam_state_change(pdev,
+					sync_ind->roamed_vdev_id,
+					WLAN_ROAM_SYNCH_IN_PROG,
+					REASON_ROAM_HANDOFF_DONE);
+
+		status = wlan_cm_roam_state_change(pdev,
+					vdev_id,
+					WLAN_MLO_ROAM_SYNCH_IN_PROG,
+					REASON_ROAM_HANDOFF_DONE);
+		return status;
+	}
 
 	/*
 	 * Get old bssid as, new AP is not updated yet and do cleanup
@@ -148,7 +166,7 @@ cm_fw_roam_sync_start_ind(struct wlan_objmgr_vdev *vdev,
 	wlan_dlm_update_bssid_connect_params(pdev,
 					     connected_bssid,
 					     DLM_AP_DISCONNECTED);
-	if (IS_ROAM_REASON_STA_KICKOUT(roam_reason)) {
+	if (IS_ROAM_REASON_STA_KICKOUT(sync_ind->roam_reason)) {
 		struct reject_ap_info ap_info;
 
 		qdf_mem_zero(&ap_info, sizeof(struct reject_ap_info));
@@ -162,9 +180,11 @@ cm_fw_roam_sync_start_ind(struct wlan_objmgr_vdev *vdev,
 	cm_update_scan_mlme_on_roam(vdev, &connected_bssid,
 				    SCAN_ENTRY_CON_STATE_NONE);
 
-	status = wlan_cm_roam_state_change(pdev, vdev_id,
-					   WLAN_ROAM_SYNCH_IN_PROG,
-					   REASON_ROAM_HANDOFF_DONE);
+	if (!MLME_IS_ROAM_SYNCH_IN_PROGRESS(psoc, vdev_id))
+		status = wlan_cm_roam_state_change(pdev,
+						   vdev_id,
+						   WLAN_ROAM_SYNCH_IN_PROG,
+						   REASON_ROAM_HANDOFF_DONE);
 
 	mlme_init_twt_context(wlan_pdev_get_psoc(pdev), &connected_bssid,
 			      TWT_ALL_SESSIONS_DIALOG_ID);
@@ -400,7 +420,7 @@ cm_fill_roam_info(struct wlan_objmgr_vdev *vdev,
 	rsp->connect_rsp.roaming_info = qdf_mem_malloc(sizeof(*roaming_info));
 	if (!rsp->connect_rsp.roaming_info)
 			return QDF_STATUS_E_NOMEM;
-	rsp->connect_rsp.vdev_id = roam_synch_data->roamed_vdev_id;
+	rsp->connect_rsp.vdev_id = wlan_vdev_get_id(vdev);
 	qdf_copy_macaddr(&rsp->connect_rsp.bssid, &roam_synch_data->bssid);
 
 	if (!util_scan_is_null_ssid(&roam_synch_data->ssid))
@@ -814,28 +834,36 @@ cm_fw_roam_sync_propagation(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id,
 	}
 
 	cm_process_roam_keys(vdev, rsp, cm_id);
-
 	mlme_cm_osif_connect_complete(vdev, connect_rsp);
-	cm_if_mgr_inform_connect_complete(cm_ctx->vdev,
-					  connect_rsp->connect_status);
-	cm_inform_blm_connect_complete(cm_ctx->vdev, connect_rsp);
-	cm_update_owe_info(vdev, connect_rsp, vdev_id);
+
+	/**
+	 * Don't send roam_sync complete for MLO link vdevs.
+	 * Send only for legacy STA/MLO STA vdev.
+	 */
+	if (!wlan_vdev_mlme_is_mlo_link_vdev(vdev)) {
+		cm_if_mgr_inform_connect_complete(cm_ctx->vdev,
+						  connect_rsp->connect_status);
+		cm_inform_blm_connect_complete(cm_ctx->vdev, connect_rsp);
+		wlan_tdls_notify_sta_connect(vdev_id,
+					mlme_get_tdls_chan_switch_prohibited(vdev),
+					mlme_get_tdls_prohibited(vdev), vdev);
+		wlan_p2p_status_connect(vdev);
+
+		if (!cm_csr_is_ss_wait_for_key(vdev_id)) {
+			mlme_debug(CM_PREFIX_FMT "WLAN link up with AP = "
+				   QDF_MAC_ADDR_FMT,
+				   CM_PREFIX_REF(vdev_id, cm_id),
+				   QDF_MAC_ADDR_REF(connect_rsp->bssid.bytes));
+			cm_roam_start_init_on_connect(pdev, vdev_id);
+		}
+		wlan_cm_tgt_send_roam_sync_complete_cmd(psoc, vdev_id);
+
+		mlo_roam_copy_partner_info(connect_rsp, roam_synch_data);
+		mlo_roam_update_connected_links(vdev, connect_rsp);
+	}
 	cm_connect_info(vdev, true, &connect_rsp->bssid, &connect_rsp->ssid,
 			connect_rsp->freq);
-	wlan_tdls_notify_sta_connect(vdev_id,
-				     mlme_get_tdls_chan_switch_prohibited(vdev),
-				     mlme_get_tdls_prohibited(vdev), vdev);
-	wlan_p2p_status_connect(vdev);
-
-	if (!cm_csr_is_ss_wait_for_key(vdev_id)) {
-		mlme_debug(CM_PREFIX_FMT "WLAN link up with AP = "
-			   QDF_MAC_ADDR_FMT,
-			   CM_PREFIX_REF(vdev_id, cm_id),
-			   QDF_MAC_ADDR_REF(connect_rsp->bssid.bytes));
-		cm_roam_start_init_on_connect(pdev, vdev_id);
-	}
 
-	wlan_cm_tgt_send_roam_sync_complete_cmd(psoc, vdev_id);
 	status = cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_ROAM_DONE,
 					  sizeof(*roam_synch_data),
 					  roam_synch_data);
@@ -855,6 +883,7 @@ error:
 	if (QDF_IS_STATUS_ERROR(status)) {
 		cm_roam_stop_req(psoc, vdev_id, REASON_ROAM_SYNCH_FAILED);
 		cm_abort_fw_roam(cm_ctx, cm_id);
+		mlo_update_connected_links(vdev, 0);
 	}
 rel_ref:
 	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_SB_ID);
@@ -938,6 +967,13 @@ QDF_STATUS cm_fw_roam_complete(struct cnx_mgr *cm_ctx, void *data)
 
 	wlan_cm_handle_sta_sta_roaming_enablement(psoc, vdev_id);
 
+	if (wlan_vdev_mlme_is_mlo_link_vdev(cm_ctx->vdev)) {
+		status = wlan_cm_roam_state_change(pdev,
+						   vdev_id,
+						   WLAN_ROAM_DEINIT,
+						   REASON_ROAM_HANDOFF_DONE);
+	}
+
 	if (roam_synch_data->auth_status == ROAM_AUTH_STATUS_AUTHENTICATED)
 		wlan_cm_roam_state_change(pdev, vdev_id,
 					  WLAN_ROAM_RSO_ENABLED,

+ 3 - 2
components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_i.h

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2022 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
@@ -115,7 +116,7 @@ QDF_STATUS cm_fw_roam_sync_req(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id,
 /**
  * cm_fw_roam_sync_start_ind() - Handle roam sync req
  * @vdev: Vdev objmgr
- * @roam_reason: Roam reason
+ * @sync_ind: Roam sync ind
  *
  * This function handles roam sync event to connection manager
  * state machine
@@ -124,7 +125,7 @@ QDF_STATUS cm_fw_roam_sync_req(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id,
  */
 QDF_STATUS
 cm_fw_roam_sync_start_ind(struct wlan_objmgr_vdev *vdev,
-			  uint8_t roam_reason);
+			  struct roam_offload_synch_ind *sync_ind);
 
 /**
  * cm_fw_roam_sync_propagation() - Post roam sync propagation to CM SM

+ 15 - 4
components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload_event.c

@@ -185,6 +185,13 @@ QDF_STATUS cm_add_fw_roam_cmd_to_list_n_ser(struct cnx_mgr *cm_ctx,
 		return status;
 	}
 
+	/**
+	 * Skip adding dummy SER command for MLO link vdev. It's expected to add
+	 * only for MLO sta in case of MLO connection
+	 */
+	if (wlan_vdev_mlme_is_mlo_link_vdev(cm_ctx->vdev))
+		return status;
+
 	status = cm_add_fw_roam_dummy_ser_cb(pdev, cm_ctx, cm_req);
 	if (QDF_IS_STATUS_ERROR(status)) {
 		cm_abort_fw_roam(cm_ctx, cm_req->roam_req.cm_id);
@@ -413,6 +420,7 @@ QDF_STATUS cm_roam_sync_event_handler_cb(struct wlan_objmgr_vdev *vdev,
 	QDF_STATUS status = QDF_STATUS_SUCCESS;
 	struct rso_config *rso_cfg;
 	uint16_t ie_len = 0;
+	uint8_t vdev_id;
 
 	sync_ind = (struct roam_offload_synch_ind *)event;
 
@@ -432,6 +440,7 @@ QDF_STATUS cm_roam_sync_event_handler_cb(struct wlan_objmgr_vdev *vdev,
 		return QDF_STATUS_E_NULL_VALUE;
 	}
 
+	vdev_id = wlan_vdev_get_id(vdev);
 	rso_cfg = wlan_cm_get_rso_config(vdev);
 	if (!rso_cfg)
 		return QDF_STATUS_E_NULL_VALUE;
@@ -446,14 +455,15 @@ QDF_STATUS cm_roam_sync_event_handler_cb(struct wlan_objmgr_vdev *vdev,
 					  QDF_PROTO_TYPE_EVENT,
 					  QDF_ROAM_SYNCH));
 
-	if (MLME_IS_ROAM_SYNCH_IN_PROGRESS(psoc, sync_ind->roamed_vdev_id)) {
+	if (MLME_IS_ROAM_SYNCH_IN_PROGRESS(psoc, sync_ind->roamed_vdev_id) &&
+	    !is_multi_link_roam(sync_ind)) {
 		mlme_err("Ignoring RSI since one is already in progress");
 		status = QDF_STATUS_E_FAILURE;
 		goto err;
 	}
 
 	if (!QDF_IS_STATUS_SUCCESS(cm_fw_roam_sync_start_ind(vdev,
-							     sync_ind->roam_reason))) {
+							     sync_ind))) {
 		mlme_err("LFR3: CSR Roam synch cb failed");
 		wlan_cm_free_roam_synch_frame_ind(rso_cfg);
 		goto err;
@@ -470,13 +480,14 @@ QDF_STATUS cm_roam_sync_event_handler_cb(struct wlan_objmgr_vdev *vdev,
 	}
 
 	if (QDF_IS_STATUS_ERROR(cm_roam_pe_sync_callback(sync_ind,
+							 vdev_id,
 							 ie_len))) {
 		mlme_err("LFR3: PE roam synch cb failed");
 		status = QDF_STATUS_E_BUSY;
 		goto err;
 	}
 
-	cm_roam_update_vdev(sync_ind);
+	cm_roam_update_vdev(sync_ind, vdev_id);
 	/*
 	 * update phy_mode in wma to avoid mismatch in phymode between host and
 	 * firmware. The phymode stored in peer->peer_mlme.phymode is
@@ -489,7 +500,7 @@ QDF_STATUS cm_roam_sync_event_handler_cb(struct wlan_objmgr_vdev *vdev,
 				  sync_ind->bssid.bytes,
 				  &sync_ind->chan);
 	cm_fw_roam_sync_propagation(psoc,
-				    sync_ind->roamed_vdev_id,
+				    vdev_id,
 				    sync_ind);
 
 err:

+ 5 - 2
components/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_roam_api.h

@@ -1494,6 +1494,7 @@ cm_roam_pmkid_request_handler(struct roam_pmkid_req_event *data);
 /**
  * cm_roam_update_vdev() - Update the STA and BSS
  * @sync_ind: Information needed for roam sync propagation
+ * @vdev_id: vdev id
  *
  * This function will perform all the vdev related operations with
  * respect to the self sta and the peer after roaming and completes
@@ -1501,12 +1502,14 @@ cm_roam_pmkid_request_handler(struct roam_pmkid_req_event *data);
  *
  * Return: None
  */
-void cm_roam_update_vdev(struct roam_offload_synch_ind *sync_ind);
+void cm_roam_update_vdev(struct roam_offload_synch_ind *sync_ind,
+			 uint8_t vdev_id);
 
 /**
  * cm_roam_pe_sync_callback() - Callback registered at pe, gets invoked when
  * ROAM SYNCH event is received from firmware
  * @sync_ind: Structure with roam synch parameters
+ * @vdev_id: vdev id
  * @len: length for bss_description
  *
  * This is a PE level callback called from CM to complete the roam synch
@@ -1517,7 +1520,7 @@ void cm_roam_update_vdev(struct roam_offload_synch_ind *sync_ind);
  */
 QDF_STATUS
 cm_roam_pe_sync_callback(struct roam_offload_synch_ind *sync_ind,
-			 uint16_t len);
+			 uint8_t vdev_id, uint16_t len);
 
 /**
  * cm_update_phymode_on_roam() - Update new phymode after

+ 18 - 0
components/umac/mlme/mlo_mgr/inc/wlan_mlo_mgr_roam.h

@@ -143,6 +143,19 @@ QDF_STATUS mlo_enable_rso(struct wlan_objmgr_pdev *pdev,
 void mlo_roam_copy_partner_info(struct wlan_cm_connect_resp *connect_rsp,
 				struct roam_offload_synch_ind *sync_ind);
 
+/**
+ * mlo_roam_update_connected_links - update connected links bitmap after roaming
+ *
+ * @vdev: vdev pointer
+ * @connect_rsp: connect resp structure pointer
+ *
+ * This api will be called to copy partner link info to connect response.
+ *
+ * Return: none
+ */
+void mlo_roam_update_connected_links(struct wlan_objmgr_vdev *vdev,
+				     struct wlan_cm_connect_resp *connect_rsp);
+
 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
 /**
  * mlo_cm_roam_sync_cb - Callback function from CM to MLO mgr
@@ -214,5 +227,10 @@ static inline void
 mlo_roam_copy_partner_info(struct wlan_cm_connect_resp *connect_rsp,
 			   struct roam_offload_synch_ind *sync_ind)
 {}
+
+static inline void
+mlo_roam_update_connected_links(struct wlan_objmgr_vdev *vdev,
+				struct wlan_cm_connect_resp *connect_rsp)
+{}
 #endif /* WLAN_FEATURE_11BE_MLO */
 #endif

+ 8 - 0
components/umac/mlme/mlo_mgr/src/wlan_mlo_mgr_roam.c

@@ -361,3 +361,11 @@ mlo_roam_copy_partner_info(struct wlan_cm_connect_resp *connect_rsp,
 	}
 }
 
+void
+mlo_roam_update_connected_links(struct wlan_objmgr_vdev *vdev,
+				struct wlan_cm_connect_resp *connect_rsp)
+{
+	mlo_update_connected_links_bmap(vdev->mlo_dev_ctx,
+					connect_rsp->ml_parnter_info);
+}
+

+ 2 - 0
core/mac/inc/sir_api.h

@@ -535,6 +535,7 @@ struct roam_pmkid_req_event;
 /**
  * typedef pe_roam_synch_fn_t - PE roam synch callback routine pointer
  * @mac_ctx: Global MAC context
+ * @vdev_id: vdev id
  * @roam_sync_ind_ptr: Structure with roam synch parameters
  * @ie_len: ie length
  * @reason: Reason for calling the callback
@@ -547,6 +548,7 @@ struct roam_pmkid_req_event;
  */
 typedef QDF_STATUS
 (*pe_roam_synch_fn_t)(struct mac_context *mac_ctx,
+		      uint8_t vdev_id,
 		      struct roam_offload_synch_ind *roam_sync_ind_ptr,
 		      uint16_t ie_len,
 		      enum sir_roam_op_code reason);

+ 3 - 0
core/mac/src/pe/include/lim_api.h

@@ -294,6 +294,7 @@ bool lim_is_sb_disconnect_allowed_fl(struct pe_session *session,
  * pe_roam_synch_callback() - Callback registered at wma, gets invoked when
  * ROAM SYNCH event is received from firmware
  * @mac_ctx: global mac context
+ * @vdev_id: VDEV id
  * @roam_sync_ind_ptr: Structure with roam synch parameters
  * @ie_len: ie length
  * @reason: Operation to be done by the callback
@@ -306,6 +307,7 @@ bool lim_is_sb_disconnect_allowed_fl(struct pe_session *session,
  */
 QDF_STATUS
 pe_roam_synch_callback(struct mac_context *mac_ctx,
+		       uint8_t vdev_id,
 		       struct roam_offload_synch_ind *roam_sync_ind_ptr,
 		       uint16_t ie_len,
 		       enum sir_roam_op_code reason);
@@ -335,6 +337,7 @@ pe_disconnect_callback(struct mac_context *mac, uint8_t vdev_id,
 #else
 static inline QDF_STATUS
 pe_roam_synch_callback(struct mac_context *mac_ctx,
+		       uint8_t vdev_id,
 		       struct roam_offload_synch_ind *roam_sync_ind_ptr,
 		       uint16_t ie_len,
 		       enum sir_roam_op_code reason)

+ 104 - 26
core/mac/src/pe/lim/lim_api.c

@@ -1982,7 +1982,8 @@ static QDF_STATUS
 lim_roam_gen_beacon_descr(struct mac_context *mac,
 			  struct roam_offload_synch_ind *roam_ind,
 			  tpSirProbeRespBeacon parsed_frm,
-			  uint8_t **ie, uint32_t *ie_len)
+			  uint8_t **ie, uint32_t *ie_len,
+			  struct qdf_mac_addr *bssid)
 {
 	QDF_STATUS status;
 	uint8_t *bcn_prb_ptr;
@@ -1995,15 +1996,18 @@ lim_roam_gen_beacon_descr(struct mac_context *mac,
 	ie_offset = SIR_MAC_HDR_LEN_3A + SIR_MAC_B_PR_SSID_OFFSET;
 
 	if (qdf_is_macaddr_zero((struct qdf_mac_addr *)mac_hdr->bssId)) {
-		pe_debug("bssid is 0 in beacon/probe update it with bssId "QDF_MAC_ADDR_FMT" in sync ind",
-			QDF_MAC_ADDR_REF(roam_ind->bssid.bytes));
-		qdf_mem_copy(mac_hdr->bssId, roam_ind->bssid.bytes,
+		pe_debug("bssid is 0 in beacon/probe update it with bssId"
+			 QDF_MAC_ADDR_FMT "in sync ind",
+			 QDF_MAC_ADDR_REF(bssid->bytes));
+		qdf_mem_copy(mac_hdr->bssId, bssid->bytes,
 			     sizeof(tSirMacAddr));
 	}
 
-	if (qdf_mem_cmp(&roam_ind->bssid.bytes,
-			&mac_hdr->bssId, QDF_MAC_ADDR_SIZE) != 0) {
-		pe_debug("LFR3:MBSSID Beacon/Prb Rsp: %d bssid "QDF_MAC_ADDR_FMT,
+	if ((!is_multi_link_roam(roam_ind)) &&
+	    (qdf_mem_cmp(bssid->bytes,
+			 &mac_hdr->bssId, QDF_MAC_ADDR_SIZE) != 0)) {
+		pe_debug("LFR3:MBSSID Beacon/Prb Rsp: %d bssid "
+			 QDF_MAC_ADDR_FMT,
 			 roam_ind->isBeacon,
 			 QDF_MAC_ADDR_REF(mac_hdr->bssId));
 		/*
@@ -2066,7 +2070,8 @@ lim_roam_gen_beacon_descr(struct mac_context *mac,
 static QDF_STATUS
 lim_roam_fill_bss_descr(struct mac_context *mac,
 			struct roam_offload_synch_ind *roam_synch_ind_ptr,
-			struct bss_description *bss_desc_ptr)
+			struct bss_description *bss_desc_ptr,
+			uint8_t vdev_id)
 {
 	uint32_t ie_len = 0;
 	tpSirProbeRespBeacon parsed_frm_ptr;
@@ -2074,6 +2079,7 @@ lim_roam_fill_bss_descr(struct mac_context *mac,
 	uint8_t *bcn_proberesp_ptr;
 	QDF_STATUS status;
 	uint8_t *ie = NULL;
+	struct qdf_mac_addr bssid;
 
 	bcn_proberesp_ptr = (uint8_t *)roam_synch_ind_ptr +
 		roam_synch_ind_ptr->beaconProbeRespOffset;
@@ -2089,10 +2095,16 @@ lim_roam_fill_bss_descr(struct mac_context *mac,
 		qdf_mem_free(parsed_frm_ptr);
 		return QDF_STATUS_E_FAILURE;
 	}
-	pe_debug("LFR3:Beacon/Prb Rsp: %d len %d bssid "QDF_MAC_ADDR_FMT" beacon "QDF_MAC_ADDR_FMT,
+
+	if (is_multi_link_roam(roam_synch_ind_ptr))
+		mlo_get_sta_link_mac_addr(vdev_id, roam_synch_ind_ptr, &bssid);
+	else
+		bssid = roam_synch_ind_ptr->bssid;
+
+	pe_debug("LFR3:Beacon/Prb Rsp: %d bssid " QDF_MAC_ADDR_FMT
+		 " beacon " QDF_MAC_ADDR_FMT,
 		 roam_synch_ind_ptr->isBeacon,
-		 roam_synch_ind_ptr->beaconProbeRespLength,
-		 QDF_MAC_ADDR_REF(roam_synch_ind_ptr->bssid.bytes),
+		 QDF_MAC_ADDR_REF(bssid.bytes),
 		 QDF_MAC_ADDR_REF(mac_hdr->bssId));
 
 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG,
@@ -2102,7 +2114,8 @@ lim_roam_fill_bss_descr(struct mac_context *mac,
 	status = lim_roam_gen_beacon_descr(mac,
 					   roam_synch_ind_ptr,
 					   parsed_frm_ptr,
-					   &ie, &ie_len);
+					   &ie, &ie_len,
+					   &bssid);
 	if (QDF_IS_STATUS_ERROR(status)) {
 		pe_err("Failed to parse beacon");
 		qdf_mem_free(parsed_frm_ptr);
@@ -2129,7 +2142,11 @@ lim_roam_fill_bss_descr(struct mac_context *mac,
 	bss_desc_ptr->rssi = roam_synch_ind_ptr->rssi;
 	/* Copy Timestamp */
 	bss_desc_ptr->scansystimensec = qdf_get_monotonic_boottime_ns();
-	if (parsed_frm_ptr->he_op.oper_info_6g_present) {
+
+	if (is_multi_link_roam(roam_synch_ind_ptr)) {
+		bss_desc_ptr->chan_freq = mlo_roam_get_chan_freq(vdev_id,
+								 roam_synch_ind_ptr);
+	} else if (parsed_frm_ptr->he_op.oper_info_6g_present) {
 		bss_desc_ptr->chan_freq = wlan_reg_chan_band_to_freq(mac->pdev,
 						parsed_frm_ptr->he_op.oper_info_6g.info.primary_ch,
 						BIT(REG_BAND_6G));
@@ -2161,7 +2178,7 @@ lim_roam_fill_bss_descr(struct mac_context *mac,
 	&bcn_proberesp_ptr[SIR_MAC_HDR_LEN_3A + SIR_MAC_B_PR_CAPAB_OFFSET], 2);
 
 	qdf_mem_copy((uint8_t *) &bss_desc_ptr->bssId,
-		     (uint8_t *)roam_synch_ind_ptr->bssid.bytes,
+		     (uint8_t *)&bssid.bytes,
 		     sizeof(tSirMacAddr));
 
 	qdf_mem_copy((uint8_t *)&bss_desc_ptr->seq_ctrl,
@@ -2478,8 +2495,50 @@ lim_check_ft_initial_im_association(struct roam_offload_synch_ind *roam_synch,
 	}
 }
 
+static QDF_STATUS
+lim_gen_link_specific_assoc_rsp(struct mac_context *mac_ctx,
+				struct pe_session *session_entry,
+				uint8_t *reassoc_rsp,
+				uint32_t reassoc_rsp_len)
+{
+	struct element_info link_reassoc_rsp;
+	struct qdf_mac_addr sta_link_addr;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	link_reassoc_rsp.ptr = qdf_mem_malloc(reassoc_rsp_len);
+	if (!link_reassoc_rsp.ptr)
+		return QDF_STATUS_E_NOMEM;
+
+	qdf_mem_copy(&sta_link_addr, session_entry->self_mac_addr,
+		     QDF_MAC_ADDR_SIZE);
+
+	link_reassoc_rsp.len = reassoc_rsp_len;
+
+	status = util_gen_link_assoc_rsp(reassoc_rsp + WLAN_MAC_HDR_LEN_3A,
+					 reassoc_rsp_len - WLAN_MAC_HDR_LEN_3A,
+					 true,
+					 sta_link_addr,
+					 link_reassoc_rsp.ptr,
+					 reassoc_rsp_len,
+					 (qdf_size_t *)&link_reassoc_rsp.len);
+
+	if (QDF_IS_STATUS_ERROR(status)) {
+		pe_err("MLO ROAM: Link reassoc generation failed %d", status);
+		goto end;
+	}
+
+	lim_process_assoc_rsp_frame(mac_ctx, link_reassoc_rsp.ptr,
+				    link_reassoc_rsp.len,
+				    LIM_REASSOC, session_entry);
+end:
+	qdf_mem_free(link_reassoc_rsp.ptr);
+	link_reassoc_rsp.len = 0;
+	return status;
+}
+
 QDF_STATUS
 pe_roam_synch_callback(struct mac_context *mac_ctx,
+		       uint8_t vdev_id,
 		       struct roam_offload_synch_ind *roam_sync_ind_ptr,
 		       uint16_t ie_len,
 		       enum sir_roam_op_code reason)
@@ -2494,13 +2553,15 @@ pe_roam_synch_callback(struct mac_context *mac_ctx,
 	QDF_STATUS status = QDF_STATUS_E_FAILURE;
 	struct bss_description *bss_desc = NULL;
 	uint16_t ric_tspec_len;
+	struct qdf_mac_addr bssid;
 
 	if (!roam_sync_ind_ptr) {
 		pe_err("LFR3:roam_sync_ind_ptr is NULL");
 		return status;
 	}
 	session_ptr = pe_find_session_by_vdev_id(mac_ctx,
-				roam_sync_ind_ptr->roamed_vdev_id);
+				vdev_id);
+
 	if (!session_ptr) {
 		pe_err("LFR3:Unable to find session");
 		return status;
@@ -2511,6 +2572,11 @@ pe_roam_synch_callback(struct mac_context *mac_ctx,
 		return status;
 	}
 
+	if (is_multi_link_roam(roam_sync_ind_ptr))
+		mlo_get_sta_link_mac_addr(vdev_id, roam_sync_ind_ptr, &bssid);
+	else
+		bssid = roam_sync_ind_ptr->bssid;
+
 	pe_debug("LFR3: PE callback reason: %d", reason);
 	switch (reason) {
 	case SIR_ROAMING_ABORT:
@@ -2541,7 +2607,7 @@ pe_roam_synch_callback(struct mac_context *mac_ctx,
 	pe_debug("LFR3:Received ROAM SYNCH IND bssid "QDF_MAC_ADDR_FMT" auth: %d vdevId: %d",
 		 QDF_MAC_ADDR_REF(roam_sync_ind_ptr->bssid.bytes),
 		 roam_sync_ind_ptr->auth_status,
-		 roam_sync_ind_ptr->roamed_vdev_id);
+		 vdev_id);
 
 	/*
 	 * If deauth from AP already in progress, ignore Roam Synch Indication
@@ -2559,7 +2625,8 @@ pe_roam_synch_callback(struct mac_context *mac_ctx,
 		return status;
 	}
 
-	status = lim_roam_fill_bss_descr(mac_ctx, roam_sync_ind_ptr, bss_desc);
+	status = lim_roam_fill_bss_descr(mac_ctx, roam_sync_ind_ptr,
+					 bss_desc, vdev_id);
 	if (!QDF_IS_STATUS_SUCCESS(status)) {
 		pe_err("LFR3:Failed to fill Bss Descr");
 		qdf_mem_free(bss_desc);
@@ -2614,20 +2681,24 @@ pe_roam_synch_callback(struct mac_context *mac_ctx,
 	lim_delete_tdls_peers(mac_ctx, session_ptr);
 	curr_sta_ds = dph_lookup_hash_entry(mac_ctx, session_ptr->bssId, &aid,
 					    &session_ptr->dph.dphHashTable);
-	if (!curr_sta_ds) {
+	if (!curr_sta_ds && !is_multi_link_roam(roam_sync_ind_ptr)) {
 		pe_err("LFR3:failed to lookup hash entry");
 		ft_session_ptr->bRoamSynchInProgress = false;
 		return status;
 	}
+
 	session_ptr->limSmeState = eLIM_SME_IDLE_STATE;
-	lim_mlo_notify_peer_disconn(session_ptr, curr_sta_ds);
-	lim_cleanup_rx_path(mac_ctx, curr_sta_ds, session_ptr, false);
-	lim_delete_dph_hash_entry(mac_ctx, curr_sta_ds->staAddr, aid,
-				  session_ptr);
+	if (curr_sta_ds) {
+		lim_mlo_notify_peer_disconn(session_ptr, curr_sta_ds);
+		lim_mlo_roam_delete_link_peer(session_ptr, curr_sta_ds);
+		lim_cleanup_rx_path(mac_ctx, curr_sta_ds, session_ptr, false);
+		lim_delete_dph_hash_entry(mac_ctx, curr_sta_ds->staAddr, aid,
+					  session_ptr);
+	}
 	pe_delete_session(mac_ctx, session_ptr);
 	session_ptr = NULL;
 	curr_sta_ds = dph_add_hash_entry(mac_ctx,
-					 roam_sync_ind_ptr->bssid.bytes,
+					 bssid.bytes,
 					 DPH_STA_HASH_INDEX_PEER,
 					 &ft_session_ptr->dph.dphHashTable);
 	if (!curr_sta_ds) {
@@ -2645,9 +2716,16 @@ pe_roam_synch_callback(struct mac_context *mac_ctx,
 
 	reassoc_resp = (uint8_t *)roam_sync_ind_ptr +
 			roam_sync_ind_ptr->reassocRespOffset;
-	lim_process_assoc_rsp_frame(mac_ctx, reassoc_resp,
-				    roam_sync_ind_ptr->reassocRespLength,
-				    LIM_REASSOC, ft_session_ptr);
+
+	if (wlan_vdev_mlme_get_is_mlo_link(mac_ctx->psoc, vdev_id))
+		lim_gen_link_specific_assoc_rsp(mac_ctx,
+						ft_session_ptr,
+						reassoc_resp,
+						roam_sync_ind_ptr->reassocRespLength);
+	else
+		lim_process_assoc_rsp_frame(mac_ctx, reassoc_resp,
+					    roam_sync_ind_ptr->reassocRespLength,
+					    LIM_REASSOC, ft_session_ptr);
 
 	lim_check_ft_initial_im_association(roam_sync_ind_ptr, ft_session_ptr);
 

+ 3 - 1
core/mac/src/pe/lim/lim_process_assoc_rsp_frame.c

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2022 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
@@ -889,6 +889,8 @@ lim_process_assoc_rsp_frame(struct mac_context *mac_ctx, uint8_t *rx_pkt_info,
 		(session_entry->limMlmState != eLIM_MLM_WT_ASSOC_RSP_STATE)) ||
 		((subtype == LIM_REASSOC) &&
 		 !lim_is_roam_synch_in_progress(mac_ctx->psoc, session_entry) &&
+		 !MLME_IS_MLO_ROAM_SYNCH_IN_PROGRESS(mac_ctx->psoc,
+						     session_entry->vdev_id) &&
 		((session_entry->limMlmState != eLIM_MLM_WT_REASSOC_RSP_STATE)
 		&& (session_entry->limMlmState !=
 		eLIM_MLM_WT_FT_REASSOC_RSP_STATE)

+ 2 - 1
core/wma/inc/wma.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2022 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
@@ -995,6 +995,7 @@ typedef struct {
 						    uint8_t vdev_id,
 						    struct qdf_mac_addr bssid);
 	QDF_STATUS (*pe_roam_synch_cb)(struct mac_context *mac,
+		uint8_t vdev_id,
 		struct roam_offload_synch_ind *roam_synch_data,
 		uint16_t ie_len,
 		enum sir_roam_op_code reason);

+ 54 - 11
core/wma/src/wma_scan_roam.c

@@ -89,6 +89,7 @@
 #endif /* FEATURE_WLAN_DIAG_SUPPORT */
 #include <../../core/src/wlan_cm_roam_i.h>
 #include "wlan_cm_roam_api.h"
+#include "wlan_mlo_mgr_roam.h"
 #ifdef FEATURE_WLAN_EXTSCAN
 #define WMA_EXTSCAN_CYCLE_WAKE_LOCK_DURATION WAKELOCK_DURATION_RECOMMENDED
 
@@ -543,14 +544,17 @@ wma_send_roam_preauth_status(tp_wma_handle wma_handle,
  */
 static void
 wma_roam_update_vdev(tp_wma_handle wma,
-		     struct roam_offload_synch_ind *roam_synch_ind_ptr)
+		     struct roam_offload_synch_ind *roam_synch_ind_ptr,
+		     uint8_t roamed_vdev_id)
 {
 	tDeleteStaParams *del_sta_params;
 	tAddStaParams *add_sta_params;
 	uint8_t vdev_id, *bssid;
 	int32_t uc_cipher, cipher_cap;
+	bool is_assoc_peer = false;
+	struct qdf_mac_addr mac_addr;
 
-	vdev_id = roam_synch_ind_ptr->roamed_vdev_id;
+	vdev_id = roamed_vdev_id;
 	wma->interfaces[vdev_id].nss = roam_synch_ind_ptr->nss;
 	/* update freq and channel width */
 	wma->interfaces[vdev_id].ch_freq =
@@ -569,13 +573,19 @@ wma_roam_update_vdev(tp_wma_handle wma,
 		return;
 	}
 
+	if (is_multi_link_roam(roam_synch_ind_ptr))
+		mlo_get_sta_link_mac_addr(vdev_id, roam_synch_ind_ptr,
+					  &mac_addr);
+	else
+		mac_addr = roam_synch_ind_ptr->bssid;
+
 	qdf_mem_zero(del_sta_params, sizeof(*del_sta_params));
 	qdf_mem_zero(add_sta_params, sizeof(*add_sta_params));
 
 	del_sta_params->smesessionId = vdev_id;
 	add_sta_params->staType = STA_ENTRY_SELF;
 	add_sta_params->smesessionId = vdev_id;
-	qdf_mem_copy(&add_sta_params->bssId, &roam_synch_ind_ptr->bssid.bytes,
+	qdf_mem_copy(&add_sta_params->bssId, &mac_addr,
 		     QDF_MAC_ADDR_SIZE);
 	add_sta_params->assocId = roam_synch_ind_ptr->aid;
 
@@ -587,19 +597,36 @@ wma_roam_update_vdev(tp_wma_handle wma,
 
 	wma_delete_sta(wma, del_sta_params);
 	wma_delete_bss(wma, vdev_id);
-	wma_create_peer(wma, roam_synch_ind_ptr->bssid.bytes,
-			WMI_PEER_TYPE_DEFAULT, vdev_id, NULL, false);
+	is_assoc_peer = wlan_vdev_mlme_get_is_mlo_vdev(wma->psoc, vdev_id);
+	if (is_multi_link_roam(roam_synch_ind_ptr)) {
+		wma_create_peer(wma, mac_addr.bytes,
+				WMI_PEER_TYPE_DEFAULT, vdev_id,
+				roam_synch_ind_ptr->bssid.bytes,
+				is_assoc_peer);
+	} else {
+		wma_create_peer(wma, mac_addr.bytes,
+				WMI_PEER_TYPE_DEFAULT,
+				vdev_id,
+				NULL,
+				is_assoc_peer);
+	}
+
+	if (is_multi_link_roam(roam_synch_ind_ptr))
+		lim_roam_mlo_create_peer(wma->mac_context,
+					 roam_synch_ind_ptr,
+					 vdev_id,
+					 mac_addr.bytes);
 
 	/* Update new peer's uc cipher */
 	uc_cipher = wlan_crypto_get_param(wma->interfaces[vdev_id].vdev,
 					   WLAN_CRYPTO_PARAM_UCAST_CIPHER);
 	cipher_cap = wlan_crypto_get_param(wma->interfaces[vdev_id].vdev,
 					   WLAN_CRYPTO_PARAM_CIPHER_CAP);
-	wma_set_peer_ucast_cipher(roam_synch_ind_ptr->bssid.bytes, uc_cipher,
+	wma_set_peer_ucast_cipher(mac_addr.bytes, uc_cipher,
 				  cipher_cap);
 	wma_add_bss_lfr3(wma, roam_synch_ind_ptr->add_bss_params);
 	wma_add_sta(wma, add_sta_params);
-	qdf_mem_copy(bssid, roam_synch_ind_ptr->bssid.bytes,
+	qdf_mem_copy(bssid, mac_addr.bytes,
 		     QDF_MAC_ADDR_SIZE);
 	lim_fill_roamed_peer_twt_caps(wma->mac_context, vdev_id,
 				      roam_synch_ind_ptr);
@@ -2559,6 +2586,7 @@ static void wma_invalid_roam_reason_handler(tp_wma_handle wma_handle,
 	roam_synch_data->roamed_vdev_id = vdev_id;
 	if (notif != CM_ROAM_NOTIF_ROAM_START)
 		wma_handle->pe_roam_synch_cb(wma_handle->mac_context,
+					     roam_synch_data->roamed_vdev_id,
 					     roam_synch_data, 0, op_code);
 
 	if (notif == CM_ROAM_NOTIF_ROAM_START)
@@ -3018,28 +3046,43 @@ QDF_STATUS wma_send_ht40_obss_scanind(tp_wma_handle wma,
 }
 
 #ifdef WLAN_FEATURE_ROAM_OFFLOAD
-void cm_roam_update_vdev(struct roam_offload_synch_ind *sync_ind)
+void cm_roam_update_vdev(struct roam_offload_synch_ind *sync_ind,
+			 uint8_t vdev_id)
 {
 	tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA);
 
 	if (!wma)
 		return;
 
-	wma_roam_update_vdev(wma, sync_ind);
+	wma_roam_update_vdev(wma, sync_ind, vdev_id);
 }
 
 QDF_STATUS
 cm_roam_pe_sync_callback(struct roam_offload_synch_ind *sync_ind,
-			 uint16_t ie_len)
+			 uint8_t vdev_id, uint16_t ie_len)
 {
 	tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA);
+	struct pe_session *pe_session;
 	QDF_STATUS status;
 
 	if (!wma)
 		return QDF_STATUS_E_INVAL;
 
+	pe_session = pe_find_session_by_vdev_id(wma->mac_context, vdev_id);
+	if (!pe_session) {
+		/* Legacy to MLO roaming: create new pe session */
+		status = lim_create_and_fill_link_session(wma->mac_context,
+							  vdev_id,
+							  sync_ind, ie_len);
+
+		if (QDF_IS_STATUS_ERROR(status)) {
+			wma_err("MLO ROAM: pe session creation failed vdev id %d",
+				vdev_id);
+			return status;
+		}
+	}
 	status = wma->pe_roam_synch_cb(wma->mac_context,
-				sync_ind, ie_len,
+				vdev_id, sync_ind, ie_len,
 				SIR_ROAM_SYNCH_PROPAGATION);
 
 	return status;