Преглед изворни кода

qcacld-3.0: Assemble ML IE to support fragmentation

The max IE length in the frame is 255, and the payload of Multi-Link
element and Per-STA profile subelement are becoming more than 255.
Add logic to fragment the ML IE and Per-STA profile subelement if their
length more than 255 when assembling the tx frames.

Change-Id: Iff2766f63edc7bc786169b2450887c708a3a4b76
CRs-Fixed: 3209293
Paul Zhang пре 2 година
родитељ
комит
559cc2f651

+ 20 - 79
core/mac/src/include/parser_api.h

@@ -198,10 +198,12 @@ enum operating_extension_identifier {
 	OP_CLASS_ID_201,
 };
 
-typedef struct sSirMultiLink_IE {
+#ifdef WLAN_FEATURE_11BE_MLO
+struct sir_multi_link_ie {
 	uint8_t num_of_mlo_ie;
-	tDot11fIEmlo_ie mlo_ie;
-} tSirMultiLink_IE, *tpSirMultiLink_IE;
+	struct wlan_mlo_ie mlo_ie;
+};
+#endif
 
 /* Structure common to Beacons & Probe Responses */
 typedef struct sSirProbeRespBeacon {
@@ -303,7 +305,9 @@ typedef struct sSirProbeRespBeacon {
 	uint8_t num_transmit_power_env;
 	tDot11fIEtransmit_power_env transmit_power_env[MAX_TPE_IES];
 	uint8_t ap_power_type;
-	tSirMultiLink_IE mlo_ie;
+#ifdef WLAN_FEATURE_11BE_MLO
+	struct sir_multi_link_ie mlo_ie;
+#endif
 	tDot11fIEWMMParams wmm_params;
 } tSirProbeRespBeacon, *tpSirProbeRespBeacon;
 
@@ -498,7 +502,9 @@ typedef struct sSirAssocRsp {
 	uint16_t hlp_data_len;
 	uint8_t hlp_data[FILS_MAX_HLP_DATA_LEN];
 #endif
-	tSirMultiLink_IE mlo_ie;
+#ifdef WLAN_FEATURE_11BE_MLO
+	struct sir_multi_link_ie mlo_ie;
+#endif
 } tSirAssocRsp, *tpSirAssocRsp;
 
 #ifdef FEATURE_WLAN_ESE
@@ -685,45 +691,15 @@ sir_convert_qos_map_configure_frame2_struct(struct mac_context *mac,
 					struct qos_map_set *pQosMapSet);
 
 #ifdef WLAN_FEATURE_11BE_MLO
-QDF_STATUS
-populate_dot11f_probe_req_mlo_ie(struct mac_context *mac_ctx,
-				 struct pe_session *session,
-				 tDot11fIEmlo_ie *mlo_ie);
-
 QDF_STATUS
 sir_convert_mlo_probe_rsp_frame2_struct(uint8_t *ml_ie,
 					uint32_t ml_ie_total_len,
-					tpSirMultiLink_IE mlo_ie_ptr);
+					struct sir_multi_link_ie *mlo_ie_ptr);
 
 QDF_STATUS
 populate_dot11f_mlo_caps(struct mac_context *mac_ctx,
 			 struct pe_session *session,
-			 tDot11fIEmlo_ie *mlo_ie);
-
-#else
-static inline QDF_STATUS
-populate_dot11f_probe_req_mlo_ie(struct mac_context *mac_ctx,
-				 struct pe_session *session,
-				 tDot11fIEmlo_ie *mlo_ie)
-{
-	return QDF_STATUS_E_NOSUPPORT;
-}
-
-static inline QDF_STATUS
-sir_convert_mlo_probe_rsp_frame2_struct(uint8_t *ml_ie,
-					uint32_t ml_ie_total_len,
-					tpSirMultiLink_IE mlo_ie_ptr)
-{
-	return QDF_STATUS_E_NOSUPPORT;
-}
-
-static inline QDF_STATUS
-populate_dot11f_mlo_caps(struct mac_context *mac_ctx,
-			 struct pe_session *session,
-			 tDot11fIEmlo_ie *mlo_ie)
-{
-	return QDF_STATUS_E_NOSUPPORT;
-}
+			 struct wlan_mlo_ie *mlo_ie);
 #endif
 
 #ifdef ANI_SUPPORT_11H
@@ -1390,7 +1366,8 @@ QDF_STATUS populate_dot11f_twt_extended_caps(struct mac_context *mac_ctx,
  * populate_dot11f_assoc_rsp_mlo_ie() - populate mlo ie for assoc response
  * @mac_ctx: Global MAC context
  * @session: PE session
- * @frm: assoc response frame
+ * @sta: Pointer to tpDphHashNode
+ * @frm: Assoc response frame
  *
  * Return: QDF_STATUS_SUCCESS of no error
  */
@@ -1403,13 +1380,11 @@ QDF_STATUS populate_dot11f_assoc_rsp_mlo_ie(struct mac_context *mac_ctx,
  * populate_dot11f_bcn_mlo_ie() - populate mlo ie for beacon
  * @mac_ctx: Global MAC context
  * @session: PE session
- * @mlo_ie: MLO IE
  *
  * Return: QDF_STATUS_SUCCESS of no error
  */
 QDF_STATUS populate_dot11f_bcn_mlo_ie(struct mac_context *mac_ctx,
-				      struct pe_session *session,
-				      tDot11fIEmlo_ie *mlo_ie);
+				      struct pe_session *session);
 
 /**
  * populate_dot11f_mlo_rnr() - populate rnr for mlo
@@ -1438,23 +1413,6 @@ void populate_dot11f_rnr_tbtt_info_16(struct mac_context *mac_ctx,
 				      tDot11fIEreduced_neighbor_report *dot11f);
 
 #else
-static inline QDF_STATUS
-populate_dot11f_assoc_rsp_mlo_ie(struct mac_context *mac_ctx,
-				 struct pe_session *session,
-				 tpDphHashNode sta,
-				 tDot11fAssocResponse *frm)
-{
-	return QDF_STATUS_SUCCESS;
-}
-
-static inline QDF_STATUS
-populate_dot11f_bcn_mlo_ie(struct mac_context *mac_ctx,
-			   struct pe_session *session,
-			   tDot11fIEmlo_ie *mlo_ie)
-{
-	return QDF_STATUS_SUCCESS;
-}
-
 static inline void populate_dot11f_mlo_rnr(
 				struct mac_context *mac_ctx,
 				struct pe_session *pe_session,
@@ -1652,38 +1610,21 @@ QDF_STATUS lim_strip_and_decode_eht_op(uint8_t *ie, uint16_t ie_len,
  */
 QDF_STATUS populate_dot11f_auth_mlo_ie(struct mac_context *mac_ctx,
 				       struct pe_session *pe_session,
-				       tDot11fIEmlo_ie *mlo_ie);
+				       struct wlan_mlo_ie *mlo_ie);
 
 /**
- * populate_dot11f_assoc_req_mlo_ie() - populate MLO Operation IE
- in assoc req
+ * populate_dot11f_assoc_req_mlo_ie() - populate MLO Operation IE in assoc req
  * @mac_ctx: Global MAC context
  * @session: PE session
- * @frm: pointer to Assoc Req IE
+ * @frm: Pointer to Assoc Req IE
  *
  * Populate the mlo IE in assoc req based on the session.
  */
 QDF_STATUS
 populate_dot11f_assoc_req_mlo_ie(struct mac_context *mac_ctx,
-				 struct pe_session *pe_session,
+				 struct pe_session *session,
 				 tDot11fAssocRequest *frm);
 
-#else
-static inline
-QDF_STATUS populate_dot11f_auth_mlo_ie(struct mac_context *mac_ctx,
-				       struct pe_session *pe_session,
-				       tDot11fIEmlo_ie *mlo_ie)
-{
-	return QDF_STATUS_E_NOSUPPORT;
-}
-
-static inline QDF_STATUS
-populate_dot11f_assoc_req_mlo_ie(struct mac_context *mac_ctx,
-					     struct pe_session *pe_session,
-					     tDot11fAssocRequest *frm)
-{
-	return QDF_STATUS_E_NOSUPPORT;
-}
 #endif
 
 /**

+ 116 - 0
core/mac/src/pe/include/lim_session.h

@@ -126,6 +126,120 @@ struct obss_detection_cfg {
 #define ADAPTIVE_11R_OUI_DATA     "\x00\x00\x00\x01"
 
 #ifdef WLAN_FEATURE_11BE_MLO
+#define WLAN_STA_PROFILE_MAX_LEN 514
+#define WLAN_MLO_IE_COM_MAX_LEN 257
+
+/**
+ * wlan_mlo_sta_profile - Per STA profile structure
+ * @num_data: the length of data
+ * @data: the Per STA profile subelement data. Subelement ID + LEN + others,
+ * if num_data more than 257, it includes the frag IE for tx; it does not
+ * include the frag IE since it has been skipped when store the IE.
+ */
+struct wlan_mlo_sta_profile {
+	uint16_t num_data;
+	uint8_t data[WLAN_STA_PROFILE_MAX_LEN];
+};
+
+/**
+ * medium_sync_delay - medium sync delay info
+ * @medium_sync_duration: medium sync duration
+ * @medium_sync_ofdm_ed_thresh: medium sync OFDM ED threshold
+ * @medium_sync_max_txop_num: medium sync max txop num
+ */
+struct medium_sync_delay {
+	uint16_t medium_sync_duration:8;
+	uint16_t medium_sync_ofdm_ed_thresh:4;
+	uint16_t medium_sync_max_txop_num:4;
+};
+
+/**
+ * eml_capabilities - EML capability info
+ * @emlsr_support: EMLSR support
+ * @emlsr_padding_delay: EMLSR padding delay
+ * @emlsr_transition_delay: EMLSR transition delay
+ * @emlmr_support: EMLSR support
+ * @emlmr_delay: EMLSR delay
+ * @transition_timeout: transition timeout
+ * @reserved: reserve
+ * @emlmr_rx_nss: EMLMR RX NSS
+ * @emlmr_tx_nss: EMLMR TX NSS
+ */
+struct eml_capabilities {
+	uint16_t emlsr_support:1;
+	uint16_t emlsr_padding_delay:3;
+	uint16_t emlsr_transition_delay:3;
+	uint16_t emlmr_support:1;
+	uint16_t emlmr_delay:3;
+	uint16_t transition_timeout:4;
+	uint16_t reserved:1;
+	uint8_t emlmr_rx_nss:4;
+	uint8_t emlmr_tx_nss:4;
+};
+
+/**
+ * mld_capabilities - MLD capability info
+ * @max_simultaneous_link_num: MAX simultaneous link num
+ * @srs_support: SRS support
+ * @tid_link_map_supported: TID link map support
+ * @str_freq_separation: STR freq separation
+ * @aar_support: AAR support
+ * @reserved: reserve
+ */
+struct mld_capabilities {
+	uint16_t max_simultaneous_link_num:4;
+	uint16_t srs_support:1;
+	uint16_t tid_link_map_supported:2;
+	uint16_t str_freq_separation:5;
+	uint16_t aar_support:1;
+	uint16_t reserved:3;
+};
+
+/**
+ * wlan_mlo_ie - wlan ML IE info
+ * @type: the variant of the ML IE
+ * @reserved: reserved
+ * @link_id_info_present: the present flag of link id info
+ * @bss_param_change_cnt_present: the present flag of bss prarm change cnt
+ * @medium_sync_delay_info_present: the present flag of medium sync delay info
+ * @eml_capab_present: the present flag of EML capability
+ * @mld_capab_present: the present flag of MLD capability
+ * @reserved_1: reserved
+ * @common_info_length: common info length
+ * @mld_mac_addr: MLD mac address
+ * @link_id: link id
+ * @bss_param_change_count: bss param change count
+ * @medium_sync_delay_info: structure of medium_sync_delay
+ * @eml_capabilities_info: structure of eml_capabilities
+ * @mld_capabilities_info: structure of mld_capabilities
+ * @num_sta_profile: the number of sta profile
+ * @sta_profile: structure of wlan_mlo_sta_profile
+ * @num_data: the length of data
+ * @data: the ML IE data, includes element ID + length + extension element ID +
+ * multi-link control and common info.
+ */
+struct wlan_mlo_ie {
+	uint16_t type:3;
+	uint16_t reserved:1;
+	uint16_t link_id_info_present:1;
+	uint16_t bss_param_change_cnt_present:1;
+	uint16_t medium_sync_delay_info_present:1;
+	uint16_t eml_capab_present:1;
+	uint16_t mld_capab_present:1;
+	uint16_t reserved_1:7;
+	uint8_t common_info_length;
+	uint8_t mld_mac_addr[6];
+	uint8_t link_id;
+	uint8_t bss_param_change_count;
+	struct medium_sync_delay medium_sync_delay_info;
+	struct eml_capabilities eml_capabilities_info;
+	struct mld_capabilities mld_capabilities_info;
+	uint16_t num_sta_profile;
+	struct wlan_mlo_sta_profile sta_profile[WLAN_MLO_MAX_VDEVS];
+	uint16_t num_data;
+	uint8_t data[WLAN_MLO_IE_COM_MAX_LEN];
+};
+
 /**
  * struct mlo_link_ie - IE per link to populate mlo ie
  * @link_ds: DS IE
@@ -693,6 +807,8 @@ struct pe_session {
 #ifdef WLAN_FEATURE_11BE_MLO
 	struct mlo_link_ie_info mlo_link_info;
 	struct mlo_partner_info ml_partner_info;
+	uint16_t mlo_ie_total_len;
+	struct wlan_mlo_ie mlo_ie;
 	bool is_emlsr_capable;
 #endif
 #endif /* WLAN_FEATURE_11BE */

+ 294 - 0
core/mac/src/pe/lim/lim_mlo.c

@@ -864,3 +864,297 @@ void lim_mlo_save_mlo_info(tpDphHashNode sta_ds,
 	qdf_mem_copy(&sta_ds->mlo_info, mlo_info, sizeof(sta_ds->mlo_info));
 }
 
+QDF_STATUS lim_fill_complete_mlo_ie(struct pe_session *session,
+				    uint16_t total_len, uint8_t *target)
+{
+	struct wlan_mlo_sta_profile *sta_prof;
+	uint16_t mlo_ie_total_len;
+	uint8_t *buf, *pbuf;
+	uint16_t i;
+	uint16_t consumed = 0;
+	uint16_t index = 0;
+	struct wlan_mlo_ie *mlo_ie;
+
+	if (!session)
+		return QDF_STATUS_E_INVAL;
+
+	mlo_ie = &session->mlo_ie;
+	if (total_len > WLAN_MAX_IE_LEN + MIN_IE_LEN)
+		mlo_ie->data[TAG_LEN_POS] = WLAN_MAX_IE_LEN;
+	else
+		mlo_ie->data[TAG_LEN_POS] = total_len - MIN_IE_LEN;
+
+	buf = qdf_mem_malloc(total_len);
+	if (!buf)
+		return QDF_STATUS_E_NOMEM;
+
+	pbuf = buf;
+	qdf_mem_copy(pbuf, mlo_ie->data, mlo_ie->num_data);
+	pbuf += mlo_ie->num_data;
+
+	for (i = 0; i < mlo_ie->num_sta_profile; i++) {
+		sta_prof = &mlo_ie->sta_profile[i];
+		qdf_mem_copy(pbuf, sta_prof->data, sta_prof->num_data);
+		pbuf += sta_prof->num_data;
+	}
+
+	target[consumed++] = buf[index++];
+	target[consumed++] = buf[index++];
+	mlo_ie_total_len = pbuf - buf - MIN_IE_LEN;
+
+	for (i = 0; i < mlo_ie_total_len; i++) {
+		if (i && i % WLAN_MAX_IE_LEN == 0) {
+			/* add flagmentation IE and length */
+			target[consumed++] = WLAN_ELEMID_FRAGMENT;
+			if ((mlo_ie_total_len - i) > WLAN_MAX_IE_LEN)
+				target[consumed++] = WLAN_MAX_IE_LEN;
+			else
+				target[consumed++] = mlo_ie_total_len - i;
+		}
+		target[consumed++] = buf[index++];
+	}
+	qdf_mem_free(buf);
+	pe_debug("pack mlo ie %d bytes, expected to copy %d bytes",
+		 consumed, total_len);
+	qdf_trace_hex_dump(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG,
+			   target, consumed);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+uint16_t lim_caculate_mlo_ie_length(struct wlan_mlo_ie *mlo_ie)
+{
+	struct wlan_mlo_sta_profile *sta_prof;
+	uint16_t total_len;
+	uint16_t i, tmp;
+
+	total_len = mlo_ie->num_data;
+	for (i = 0; i < mlo_ie->num_sta_profile; i++) {
+		sta_prof = &mlo_ie->sta_profile[i];
+		total_len += sta_prof->num_data;
+	}
+
+	if (total_len > WLAN_MAX_IE_LEN + MIN_IE_LEN) {
+		/* ML IE max length  WLAN_MAX_IE_LEN + MIN_IE_LEN */
+		tmp = total_len - (WLAN_MAX_IE_LEN + MIN_IE_LEN);
+		while (tmp > WLAN_MAX_IE_LEN) {
+			/* add one flagmentation IE */
+			total_len += MIN_IE_LEN;
+			tmp -= WLAN_MAX_IE_LEN;
+		}
+		/* add one flagmentation IE */
+		total_len += MIN_IE_LEN;
+	}
+	return total_len;
+}
+
+QDF_STATUS lim_store_mlo_ie_raw_info(uint8_t *ie, uint8_t *sta_prof_ie,
+				     uint32_t total_len,
+				     struct wlan_mlo_ie *mlo_ie)
+{
+	uint8_t i, frag_num = 0, sta_index;
+	/* ml_ie_len = total_len - 2 * frag_num, does not include
+	 * WLAN_ELEMID_FRAGMENT IE and LEN
+	 */
+	uint32_t ml_ie_len;
+	uint32_t index, copied;
+	uint8_t *pfrm;
+	uint8_t *buf;
+	struct wlan_mlo_sta_profile *sta_prof;
+	uint8_t *sta_data;
+	/* Per STA profile frag or not */
+	bool frag = FALSE;
+
+	if (!ie)
+		return QDF_STATUS_E_INVAL;
+
+	qdf_mem_zero(mlo_ie, sizeof(*mlo_ie));
+
+	/* assume element ID + LEN + extension element ID + multi-link control +
+	 * common info length always less than WLAN_MAX_IE_LEN
+	 */
+	mlo_ie->num_data = sta_prof_ie - ie;
+	if (mlo_ie->num_data > WLAN_MLO_IE_COM_MAX_LEN) {
+		mlo_ie->num_data = 0;
+		return QDF_STATUS_E_INVAL;
+	}
+	qdf_mem_copy(mlo_ie->data, ie, mlo_ie->num_data);
+
+	/* Count how many frag IE */
+	pfrm = ie;
+	ml_ie_len = pfrm[TAG_LEN_POS] + MIN_IE_LEN;
+	while (ml_ie_len < total_len) {
+		frag_num++;
+		pfrm += MIN_IE_LEN + pfrm[TAG_LEN_POS];
+		ml_ie_len += pfrm[TAG_LEN_POS] + MIN_IE_LEN;
+	}
+	ml_ie_len = total_len - frag_num * MIN_IE_LEN;
+
+	buf = qdf_mem_malloc(total_len);
+	if (!buf)
+		return QDF_STATUS_E_NOMEM;
+
+	/* Copy the raw info and skip frag IE */
+	index = 0;
+	copied = 0;
+	buf[index++] = ie[copied++];
+	buf[index++] = ie[copied++];
+	for (i = 0; i < ml_ie_len - MIN_IE_LEN; i++) {
+		/* skip the frag IE */
+		if (i && i % WLAN_MAX_IE_LEN == 0)
+			copied += MIN_IE_LEN;
+		buf[index++] = ie[copied++];
+	}
+
+	/* copy sta profile from buf, it has copied the common info */
+	index = 0;
+	sta_index = 0;
+	copied = mlo_ie->num_data;
+	pfrm = buf + copied;
+	while (copied < ml_ie_len && sta_index < WLAN_MLO_MAX_VDEVS &&
+	       pfrm[ID_POS] == WLAN_ML_BV_LINFO_SUBELEMID_PERSTAPROFILE) {
+		sta_prof = &mlo_ie->sta_profile[sta_index++];
+		sta_data = sta_prof->data;
+
+		sta_data[index++] = buf[copied++];
+		sta_data[index++] = buf[copied++];
+		do {
+			if (index + pfrm[TAG_LEN_POS] >
+						WLAN_STA_PROFILE_MAX_LEN) {
+				qdf_mem_free(buf);
+				pe_debug("no enough buf to store sta prof");
+				return QDF_STATUS_E_INVAL;
+			}
+
+			for (i = 0; i < pfrm[TAG_LEN_POS]; i++)
+				sta_data[index++] = buf[copied++];
+			sta_prof->num_data = index;
+
+			if (copied < ml_ie_len &&
+			    pfrm[TAG_LEN_POS] == WLAN_MAX_IE_LEN &&
+			    pfrm[WLAN_MAX_IE_LEN + MIN_IE_LEN] ==
+					WLAN_ML_BV_LINFO_SUBELEMID_FRAGMENT) {
+				frag = TRUE;
+				/* skip sta profile frag IE */
+				copied += MIN_IE_LEN;
+			} else {
+				frag = FALSE;
+			}
+			pfrm += pfrm[TAG_LEN_POS] + MIN_IE_LEN;
+		} while (frag);
+	}
+
+	mlo_ie->num_sta_profile = sta_index;
+	qdf_mem_free(buf);
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS lim_add_frag_ie_for_sta_profile(uint8_t *data, uint16_t *len)
+{
+	uint16_t total_len;
+	uint16_t tmp, i;
+	uint8_t *buf;
+	uint16_t consumed = 0;
+	uint16_t index = 0;
+
+	total_len = *len;
+	buf = qdf_mem_malloc(total_len);
+	if (!buf)
+		return QDF_STATUS_E_NOMEM;
+
+	qdf_mem_copy(buf, data, total_len);
+
+	if (total_len > WLAN_MAX_IE_LEN + MIN_IE_LEN) {
+		/* ML IE max length  WLAN_MAX_IE_LEN + MIN_IE_LEN */
+		tmp = total_len - (WLAN_MAX_IE_LEN + MIN_IE_LEN);
+		while (tmp > WLAN_MAX_IE_LEN) {
+			/* add one flagmentation IE */
+			total_len += MIN_IE_LEN;
+			tmp -= WLAN_MAX_IE_LEN;
+		}
+		/* add one flagmentation IE */
+		total_len += MIN_IE_LEN;
+	}
+
+	data[consumed++] = buf[index++];
+	data[consumed++] = buf[index++];
+	for (i = 0; i < (*len - MIN_IE_LEN); i++) {
+		data[consumed++] = buf[index++];
+		if (i && i % WLAN_MAX_IE_LEN == 0) {
+			data[consumed++] = WLAN_ML_BV_LINFO_SUBELEMID_FRAGMENT;
+			if ((*len - MIN_IE_LEN - i) > WLAN_MAX_IE_LEN)
+				data[consumed++] = WLAN_MAX_IE_LEN;
+			else
+				data[consumed++] = *len - MIN_IE_LEN - i;
+		}
+	}
+
+	*len = total_len;
+	qdf_mem_free(buf);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+uint16_t
+lim_send_assoc_req_mgmt_frame_mlo(struct mac_context *mac_ctx,
+				  struct pe_session *session,
+				  tDot11fAssocRequest *frm)
+{
+	QDF_STATUS status;
+
+	session->mlo_ie_total_len = 0;
+	qdf_mem_zero(&session->mlo_ie, sizeof(session->mlo_ie));
+	if ((wlan_vdev_mlme_get_opmode(session->vdev) == QDF_STA_MODE) &&
+	    wlan_vdev_mlme_is_mlo_vdev(session->vdev)) {
+		status =
+			populate_dot11f_assoc_req_mlo_ie(mac_ctx, session, frm);
+		if (QDF_IS_STATUS_SUCCESS(status))
+			session->mlo_ie_total_len =
+				lim_caculate_mlo_ie_length(&session->mlo_ie);
+	}
+
+	return session->mlo_ie_total_len;
+}
+
+uint16_t
+lim_send_assoc_rsp_mgmt_frame_mlo(struct mac_context *mac_ctx,
+				  struct pe_session *session,
+				  tpDphHashNode sta,
+				  tDot11fAssocResponse *frm)
+{
+	QDF_STATUS status;
+
+	session->mlo_ie_total_len = 0;
+	qdf_mem_zero(&session->mlo_ie, sizeof(session->mlo_ie));
+	status = populate_dot11f_assoc_rsp_mlo_ie(mac_ctx, session, sta, frm);
+	if (QDF_IS_STATUS_SUCCESS(status))
+		session->mlo_ie_total_len =
+				lim_caculate_mlo_ie_length(&session->mlo_ie);
+
+	return session->mlo_ie_total_len;
+}
+
+uint16_t
+lim_send_bcn_frame_mlo(struct mac_context *mac_ctx,
+		       struct pe_session *session)
+{
+	QDF_STATUS status;
+
+	session->mlo_ie_total_len = 0;
+	qdf_mem_zero(&session->mlo_ie, sizeof(session->mlo_ie));
+	status = populate_dot11f_bcn_mlo_ie(mac_ctx, session);
+	if (QDF_IS_STATUS_SUCCESS(status))
+		session->mlo_ie_total_len =
+				lim_caculate_mlo_ie_length(&session->mlo_ie);
+
+	return session->mlo_ie_total_len;
+}
+
+uint16_t
+lim_get_frame_mlo_ie_len(struct pe_session *session)
+{
+	if (session)
+		return session->mlo_ie_total_len;
+	else
+		return 0;
+}

+ 136 - 1
core/mac/src/pe/lim/lim_mlo.h

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 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 above
@@ -228,8 +229,100 @@ QDF_STATUS lim_mlo_assoc_ind_upper_layer(struct mac_context *mac,
 					 struct mlo_partner_info *mlo_info);
 void lim_mlo_save_mlo_info(tpDphHashNode sta_ds,
 			   struct mlo_partner_info *mlo_info);
-#else
 
+/**
+ * lim_add_frag_ie_for_sta_profile() - add frag IE if STA prof len more than 255
+ * @data: sta profile ie data
+ * @len: the length of the data
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS lim_add_frag_ie_for_sta_profile(uint8_t *data, uint16_t *len);
+
+/**
+ * lim_fill_complete_mlo_ie() - fill mlo ie to target buffer
+ * @session: pointer to pe_session
+ * @total_len: the total bytes to fill target buffer
+ * @target: the buffer to fill data
+ *
+ * It also will insert the frag IE WLAN_ELEMID_FRAGMENT if the ML IE's length
+ * more than 255 bytes.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS lim_fill_complete_mlo_ie(struct pe_session *session,
+				    uint16_t total_len, uint8_t *target);
+
+/**
+ * lim_caculate_mlo_ie_length() - calculate the ML IE length
+ * @mlo_ie: the pointer to wlan_mlo_ie
+ *
+ * It tries to add the len of frag IE WLAN_ELEMID_FRAGMENT if the ML IE
+ * length more than 255 bytes.
+ *
+ * Return: QDF_STATUS
+ */
+uint16_t lim_caculate_mlo_ie_length(struct wlan_mlo_ie *mlo_ie);
+
+/**
+ * lim_send_assoc_req_mgmt_frame_mlo() - Prepare ML IE for assoc req frame
+ * @mac_ctx: pointer to mac_context
+ * @session: pointer to pe_session
+ * @frm: pointer to tDot11fAssocRequest
+ *
+ * Return: the actual ML IE length
+ */
+uint16_t
+lim_send_assoc_req_mgmt_frame_mlo(struct mac_context *mac_ctx,
+				  struct pe_session *session,
+				  tDot11fAssocRequest *frm);
+
+/**
+ * lim_send_assoc_rsq_mgmt_frame_mlo() - Prepare ML IE for assoc rsq frame
+ * @mac_ctx: pointer to mac_context
+ * @session: pointer to pe_session
+ * @sta: pointer to tpDphHashNode
+ * @frm: pointer to tDot11fAssocRequest
+ *
+ * Return: the actual ML IE length
+ */
+uint16_t
+lim_send_assoc_rsp_mgmt_frame_mlo(struct mac_context *mac_ctx,
+				  struct pe_session *session,
+				  tpDphHashNode sta,
+				  tDot11fAssocResponse *frm);
+
+/**
+ * lim_send_bcn_frame_mlo() - Prepare ML IE for beacon frame
+ * @mac_ctx: pointer to mac_context
+ * @session: pointer to pe_session
+ *
+ * Return: the actual ML IE length
+ */
+uint16_t
+lim_send_bcn_frame_mlo(struct mac_context *mac_ctx, struct pe_session *session);
+
+/**
+ * lim_get_frame_mlo_ie_len() - get ML IE length
+ * @session: pointer to pe_session
+ *
+ * Return: the actual ML IE length
+ */
+uint16_t lim_get_frame_mlo_ie_len(struct pe_session *session);
+
+/**
+ * lim_store_mlo_ie_raw_info() - store the ML IE raw info
+ * @ie: pointer the ML IE
+ * @sta_prof_ie: pointer to the first per STA prof
+ * @total_len: the length of ML IE
+ * @mlo_ie: the pointer to wlan_mlo_ie
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS lim_store_mlo_ie_raw_info(uint8_t *ie, uint8_t *sta_prof_ie,
+				     uint32_t total_len,
+				     struct wlan_mlo_ie *mlo_ie);
+#else
 static inline void lim_mlo_notify_peer_disconn(struct pe_session *pe_session,
 					       tpDphHashNode sta_ds)
 {
@@ -298,5 +391,47 @@ static inline void lim_mlo_save_mlo_info(tpDphHashNode sta_ds,
 					 struct mlo_partner_info *mlo_info)
 {
 }
+
+static inline
+QDF_STATUS lim_add_frag_ie_for_sta_profile(uint8_t *data, uint16_t *len)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline
+QDF_STATUS lim_fill_complete_mlo_ie(struct pe_session *session,
+				    uint16_t total_len, uint8_t *target)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline uint16_t
+lim_send_assoc_req_mgmt_frame_mlo(struct mac_context *mac_ctx,
+				  struct pe_session *session,
+				  tDot11fAssocRequest *frm)
+{
+	return 0;
+}
+
+static inline uint16_t
+lim_send_assoc_rsp_mgmt_frame_mlo(struct mac_context *mac_ctx,
+				  struct pe_session *session,
+				  tpDphHashNode sta,
+				  tDot11fAssocResponse *frm)
+{
+	return 0;
+}
+
+static inline uint16_t
+lim_send_bcn_frame_mlo(struct mac_context *mac_ctx, struct pe_session *session)
+{
+	return 0;
+}
+
+static inline
+uint16_t lim_get_frame_mlo_ie_len(struct pe_session *session)
+{
+	return 0;
+}
 #endif
 #endif

+ 3 - 2
core/mac/src/pe/lim/lim_process_beacon_frame.c

@@ -89,8 +89,9 @@ void lim_process_beacon_mlo(struct mac_context *mac_ctx,
 		per_sta_pro_len =
 			bcn_ptr->mlo_ie.mlo_ie.sta_profile[i].num_data;
 		stacontrol = *(uint16_t *)per_sta_pro;
-		sta_pro = per_sta_pro + 2; /* sta control */
-		sta_pro_len = per_sta_pro_len - 2;
+		/* IE ID + LEN + STA control */
+		sta_pro = per_sta_pro + MIN_IE_LEN + 2;
+		sta_pro_len = per_sta_pro_len - MIN_IE_LEN - 2;
 		link_id = QDF_GET_BITS(
 			    stacontrol,
 			    WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,

+ 69 - 40
core/mac/src/pe/lim/lim_send_management_frames.c

@@ -746,6 +746,7 @@ lim_send_probe_rsp_mgmt_frame(struct mac_context *mac_ctx,
 	bool is_vht_enabled = false;
 	tDot11fIEExtCap extracted_ext_cap = {0};
 	bool extracted_ext_cap_flag = false;
+	uint16_t mlo_ie_len = 0;
 
 	/* We don't answer requests in this case*/
 	if (ANI_DRIVER_TYPE(mac_ctx) == QDF_DRIVER_TYPE_MFG)
@@ -882,8 +883,7 @@ lim_send_probe_rsp_mgmt_frame(struct mac_context *mac_ctx,
 	}
 
 	if (wlan_vdev_mlme_is_mlo_ap(pe_session->vdev)) {
-		populate_dot11f_bcn_mlo_ie(mac_ctx, pe_session,
-					   &frm->mlo_ie);
+		mlo_ie_len = lim_send_bcn_frame_mlo(mac_ctx, pe_session);
 		populate_dot11f_mlo_rnr(mac_ctx, pe_session,
 					&frm->reduced_neighbor_report);
 	}
@@ -997,7 +997,7 @@ lim_send_probe_rsp_mgmt_frame(struct mac_context *mac_ctx,
 			status);
 	}
 
-	bytes += payload + sizeof(tSirMacMgmtHdr);
+	bytes += payload + sizeof(tSirMacMgmtHdr) + mlo_ie_len;
 
 	qdf_status = cds_packet_alloc((uint16_t) bytes, (void **)&frame,
 				      (void **)&packet);
@@ -1030,6 +1030,16 @@ lim_send_probe_rsp_mgmt_frame(struct mac_context *mac_ctx,
 		pe_warn("Probe Response pack warning (0x%08x)", status);
 	}
 
+	if (mlo_ie_len) {
+		qdf_status = lim_fill_complete_mlo_ie(pe_session, mlo_ie_len,
+				    frame + sizeof(tSirMacMgmtHdr) + payload);
+		if (QDF_IS_STATUS_ERROR(qdf_status)) {
+			pe_debug("assemble ml ie error");
+			mlo_ie_len = 0;
+		}
+		payload += mlo_ie_len;
+	}
+
 	pe_debug("Sending Probe Response frame to");
 	lim_print_mac_addr(mac_ctx, peer_macaddr, LOGD);
 
@@ -1586,6 +1596,7 @@ lim_send_assoc_rsp_mgmt_frame(struct mac_context *mac_ctx,
 	uint8_t *eht_cap_ie = NULL, eht_cap_ie_len = 0;
 	bool is_band_2g;
 	uint16_t ie_buf_size;
+	uint16_t mlo_ie_len = 0;
 
 	if (!pe_session) {
 		pe_err("pe_session is NULL");
@@ -1840,8 +1851,9 @@ lim_send_assoc_rsp_mgmt_frame(struct mac_context *mac_ctx,
 	    lim_is_session_eht_capable(pe_session) &&
 	    wlan_vdev_mlme_is_mlo_ap(pe_session->vdev)) {
 		pe_debug("Populate mlo IEs");
-		populate_dot11f_assoc_rsp_mlo_ie(mac_ctx, pe_session,
-						 sta, &frm);
+		mlo_ie_len = lim_send_assoc_rsp_mgmt_frame_mlo(mac_ctx,
+							       pe_session,
+							       sta, &frm);
 	}
 
 	/* Allocate a buffer for this frame: */
@@ -1855,7 +1867,7 @@ lim_send_assoc_rsp_mgmt_frame(struct mac_context *mac_ctx,
 			status);
 	}
 
-	bytes += sizeof(tSirMacMgmtHdr) + payload;
+	bytes += sizeof(tSirMacMgmtHdr) + payload + mlo_ie_len;
 
 	if (sta)
 		bytes += sta->mlmStaContext.owe_ie_len;
@@ -1966,15 +1978,29 @@ lim_send_assoc_rsp_mgmt_frame(struct mac_context *mac_ctx,
 		bytes = bytes + payload;
 	}
 
-	if (addn_ie_len && addn_ie_len <= WNI_CFG_ASSOC_RSP_ADDNIE_DATA_LEN)
+	if (addn_ie_len && addn_ie_len <= WNI_CFG_ASSOC_RSP_ADDNIE_DATA_LEN) {
 		qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload,
 			     &add_ie[0], addn_ie_len);
+		payload += addn_ie_len;
+	}
 
-	if (sta && sta->mlmStaContext.owe_ie_len)
-		qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload
-			     + addn_ie_len,
+	if (sta && sta->mlmStaContext.owe_ie_len) {
+		qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload,
 			     sta->mlmStaContext.owe_ie,
 			     sta->mlmStaContext.owe_ie_len);
+		payload += sta->mlmStaContext.owe_ie_len;
+	}
+
+	if (sta && mlo_ie_len) {
+		qdf_status = lim_fill_complete_mlo_ie(pe_session, mlo_ie_len,
+				      frame + sizeof(tSirMacMgmtHdr) + payload);
+		if (QDF_IS_STATUS_ERROR(qdf_status)) {
+			pe_debug("assemble ml ie error");
+			mlo_ie_len = 0;
+		}
+		payload += mlo_ie_len;
+	}
+
 	pe_nofl_debug("Assoc rsp TX: vdev %d subtype %d to "QDF_MAC_ADDR_FMT" seq num %d status %d aid %d addn_ie_len %d ht %d vht %d vendor vht %d he %d eht %d",
 		      pe_session->vdev_id, subtype,
 		      QDF_MAC_ADDR_REF(mac_hdr->da),
@@ -2371,25 +2397,6 @@ QDF_STATUS lim_fill_adaptive_11r_ie(struct pe_session *pe_session,
 }
 #endif
 
-#ifdef WLAN_FEATURE_11BE_MLO
-static void
-lim_send_assoc_req_mgmt_frame_mlo(struct mac_context *mac_ctx,
-				  struct pe_session *pe_session,
-				  tDot11fAssocRequest *frm)
-{
-	if ((wlan_vdev_mlme_get_opmode(pe_session->vdev) == QDF_STA_MODE) &&
-	    wlan_vdev_mlme_is_mlo_vdev(pe_session->vdev))
-		populate_dot11f_assoc_req_mlo_ie(mac_ctx, pe_session, frm);
-}
-#else /* WLAN_FEATURE_11BE_MLO */
-static inline void
-lim_send_assoc_req_mgmt_frame_mlo(struct mac_context *mac_ctx,
-				  struct pe_session *pe_session,
-				  tDot11fAssocRequest *frm)
-{
-}
-#endif /* WLAN_FEATURE_11BE_MLO */
-
 /**
  * lim_send_assoc_req_mgmt_frame() - Send association request
  * @mac_ctx: Handle to MAC context
@@ -2440,6 +2447,7 @@ lim_send_assoc_req_mgmt_frame(struct mac_context *mac_ctx,
 	int8_t peer_rssi = 0;
 	bool is_band_2g;
 	uint16_t ie_buf_size;
+	uint16_t mlo_ie_len;
 
 	if (!pe_session) {
 		pe_err("pe_session is NULL");
@@ -2671,7 +2679,9 @@ lim_send_assoc_req_mgmt_frame(struct mac_context *mac_ctx,
 
 	if (lim_is_session_eht_capable(pe_session))
 		populate_dot11f_eht_caps(mac_ctx, pe_session, &frm->eht_cap);
-	lim_send_assoc_req_mgmt_frame_mlo(mac_ctx, pe_session, frm);
+
+	mlo_ie_len =
+		 lim_send_assoc_req_mgmt_frame_mlo(mac_ctx, pe_session, frm);
 
 	if (pe_session->is11Rconnection) {
 		struct bss_description *bssdescr;
@@ -2899,7 +2909,8 @@ lim_send_assoc_req_mgmt_frame(struct mac_context *mac_ctx,
 
 	bytes = payload + sizeof(tSirMacMgmtHdr) + aes_block_size_len +
 		rsnx_ie_len + mbo_ie_len + adaptive_11r_ie_len +
-		mscs_ext_ie_len + vendor_ie_len;
+		mscs_ext_ie_len + vendor_ie_len + mlo_ie_len;
+
 
 	qdf_status = cds_packet_alloc((uint16_t) bytes, (void **)&frame,
 				(void **)&packet);
@@ -3007,6 +3018,16 @@ lim_send_assoc_req_mgmt_frame(struct mac_context *mac_ctx,
 		     adaptive_11r_ie, adaptive_11r_ie_len);
 	payload = payload + adaptive_11r_ie_len;
 
+	if (mlo_ie_len) {
+		qdf_status = lim_fill_complete_mlo_ie(pe_session, mlo_ie_len,
+				      frame + sizeof(tSirMacMgmtHdr) + payload);
+		if (QDF_IS_STATUS_ERROR(qdf_status)) {
+			pe_debug("assemble ml ie error");
+			mlo_ie_len = 0;
+		}
+		payload = payload + mlo_ie_len;
+	}
+
 	if (pe_session->assoc_req) {
 		qdf_mem_free(pe_session->assoc_req);
 		pe_session->assoc_req = NULL;
@@ -3254,18 +3275,23 @@ static uint32_t lim_populate_auth_mlo_ie(struct mac_context *mac_ctx,
 					 tSirMacAddr peer_addr,
 					 uint8_t *mlo_ie_buf)
 {
-	tDot11fIEmlo_ie mlo_ie;
+	struct wlan_mlo_ie *mlo_ie;
 	uint32_t mlo_ie_len = 0;
 	struct tLimPreAuthNode *auth_node;
+	QDF_STATUS status;
 
+	mlo_ie = &session->mlo_ie;
 	if (wlan_vdev_mlme_is_mlo_vdev(session->vdev)) {
-		qdf_mem_zero(&mlo_ie, sizeof(tDot11fIEmlo_ie));
+		qdf_mem_zero(mlo_ie, sizeof(*mlo_ie));
 
 		pe_debug("Auth TX sys role: %d", GET_LIM_SYSTEM_ROLE(session));
 		if (LIM_IS_STA_ROLE(session)) {
-			populate_dot11f_auth_mlo_ie(mac_ctx, session, &mlo_ie);
-			dot11f_pack_ie_mlo_ie(mac_ctx, &mlo_ie, mlo_ie_buf,
-					      DOT11F_EID_MLO_IE, &mlo_ie_len);
+			populate_dot11f_auth_mlo_ie(mac_ctx, session, mlo_ie);
+			mlo_ie_len = lim_caculate_mlo_ie_length(mlo_ie);
+			status = lim_fill_complete_mlo_ie(session, mlo_ie_len,
+							  mlo_ie_buf);
+			if (QDF_IS_STATUS_ERROR(status))
+				mlo_ie_len = 0;
 		} else if (LIM_IS_AP_ROLE(session)) {
 			auth_node = lim_search_pre_auth_list(mac_ctx, peer_addr);
 			if (!auth_node) {
@@ -3280,10 +3306,13 @@ static uint32_t lim_populate_auth_mlo_ie(struct mac_context *mac_ctx,
 			 */
 			if (auth_node && auth_node->is_mlo_ie_present) {
 				populate_dot11f_auth_mlo_ie(mac_ctx, session,
-							    &mlo_ie);
-				dot11f_pack_ie_mlo_ie(
-					mac_ctx, &mlo_ie, mlo_ie_buf,
-					DOT11F_EID_MLO_IE, &mlo_ie_len);
+							    mlo_ie);
+				mlo_ie_len = lim_caculate_mlo_ie_length(mlo_ie);
+				status = lim_fill_complete_mlo_ie(session,
+								  mlo_ie_len,
+								  mlo_ie_buf);
+				if (QDF_IS_STATUS_ERROR(status))
+					mlo_ie_len = 0;
 			}
 		}
 	}

+ 5 - 8
core/mac/src/pe/lim/lim_utils.c

@@ -7996,14 +7996,11 @@ void lim_set_mlo_caps(struct mac_context *mac, struct pe_session *session,
 		      uint8_t *ie_start, uint32_t num_bytes)
 {
 	const uint8_t *ie = NULL;
-	tDot11fIEmlo_ie dot11_cap;
+	struct wlan_mlo_ie dot11_cap;
 	struct wlan_mlo_ie_info *mlo_ie_info;
 
 	populate_dot11f_mlo_caps(mac, session, &dot11_cap);
 
-	if (!dot11_cap.present)
-		return;
-
 	ie = wlan_get_ext_ie_ptr_from_ext_id(MLO_IE_OUI_TYPE,
 					     MLO_IE_OUI_SIZE,
 					     ie_start, num_bytes);
@@ -8869,7 +8866,7 @@ void lim_update_stads_eht_bw_320mhz(struct pe_session *session,
 void lim_update_stads_emlsr_caps(struct mac_context *mac_ctx,
 				 tpDphHashNode sta_ds, tpSirAssocRsp assoc_rsp)
 {
-	if (!assoc_rsp->mlo_ie.mlo_ie.eml_capabilities.info.emlsr_support) {
+	if (!assoc_rsp->mlo_ie.mlo_ie.eml_capabilities_info.emlsr_support) {
 		pe_debug("eMLSR cap not present in assoc rsp");
 		sta_ds->mlmStaContext.emlsr_capable = false;
 		return;
@@ -8883,12 +8880,12 @@ void lim_intersect_ap_emlsr_caps(struct pe_session *session,
 				 tpSirAssocRsp assoc_rsp)
 {
 	if (session->is_emlsr_capable && assoc_rsp &&
-	    assoc_rsp->mlo_ie.mlo_ie.eml_capabilities.info.emlsr_support) {
+	    assoc_rsp->mlo_ie.mlo_ie.eml_capabilities_info.emlsr_support) {
 		add_bss->staContext.emlsr_support = true;
 		add_bss->staContext.link_id =
-		    assoc_rsp->mlo_ie.mlo_ie.link_id_info.info.link_id;
+		    assoc_rsp->mlo_ie.mlo_ie.link_id;
 		add_bss->staContext.emlsr_trans_timeout =
-		    assoc_rsp->mlo_ie.mlo_ie.eml_capabilities.info.transition_timeout;
+		    assoc_rsp->mlo_ie.mlo_ie.eml_capabilities_info.transition_timeout;
 	} else {
 		add_bss->staContext.emlsr_support = false;
 	}

+ 14 - 4
core/mac/src/pe/sch/sch_api.c

@@ -46,10 +46,7 @@
 #include "lim_utils.h"
 
 #include "wma_types.h"
-
-#ifdef WLAN_FEATURE_11BE_MLO
 #include "lim_mlo.h"
-#endif
 
 #include <target_if_vdev_mgr_tx_ops.h>
 #include <wlan_cmn_ieee80211.h>
@@ -718,6 +715,7 @@ uint32_t lim_send_probe_rsp_template_to_hal(struct mac_context *mac,
 	tDot11fProbeResponse *prb_rsp_frm;
 	QDF_STATUS status;
 	uint16_t addn_ielen = 0;
+	uint16_t mlo_ie_len;
 
 	/* Check if probe response IE is present or not */
 	addnIEPresent = (pe_session->add_ie_params.probeRespDataLen != 0);
@@ -793,7 +791,8 @@ uint32_t lim_send_probe_rsp_template_to_hal(struct mac_context *mac,
 			nStatus);
 	}
 
-	nBytes += nPayload + sizeof(tSirMacMgmtHdr);
+	mlo_ie_len = lim_get_frame_mlo_ie_len(pe_session);
+	nBytes += nPayload + sizeof(tSirMacMgmtHdr) + mlo_ie_len;
 
 	if (addnIEPresent) {
 		if ((nBytes + addn_ielen) <= SIR_MAX_PROBE_RESP_SIZE)
@@ -839,6 +838,17 @@ uint32_t lim_send_probe_rsp_template_to_hal(struct mac_context *mac,
 			"robe Response (0x%08x)", nStatus);
 	}
 
+	if (mlo_ie_len) {
+		status = lim_fill_complete_mlo_ie(pe_session, mlo_ie_len,
+					 pFrame2Hal + sizeof(tSirMacMgmtHdr) +
+					      nPayload);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			pe_debug("assemble ml ie error");
+			mlo_ie_len = 0;
+		}
+		nPayload += mlo_ie_len;
+	}
+
 	if (addnIEPresent) {
 		qdf_mem_copy(&pFrame2Hal[nBytes - addn_ielen],
 			     &addIE[0], addn_ielen);

+ 13 - 3
core/mac/src/pe/sch/sch_beacon_gen.c

@@ -42,6 +42,7 @@
 
 #include "parser_api.h"
 #include "wlan_utility.h"
+#include "lim_mlo.h"
 
 /* Offset of Channel Switch count field in CSA/ECSA IE */
 #define SCH_CSA_SWITCH_COUNT_OFFSET 2
@@ -502,6 +503,7 @@ sch_set_fixed_beacon_fields(struct mac_context *mac_ctx, struct pe_session *sess
 	uint8_t *eht_cap_ie = NULL, eht_cap_ie_len = 0;
 	bool is_band_2g;
 	uint16_t ie_buf_size;
+	uint16_t mlo_ie_len = 0;
 
 	bcn_1 = qdf_mem_malloc(sizeof(tDot11fBeacon1));
 	if (!bcn_1)
@@ -519,7 +521,6 @@ sch_set_fixed_beacon_fields(struct mac_context *mac_ctx, struct pe_session *sess
 		qdf_mem_free(bcn_2);
 		return QDF_STATUS_E_NOMEM;
 	}
-
 	/*
 	 * First set the fixed fields:
 	 * set the TFP headers, set the mac header
@@ -807,8 +808,7 @@ sch_set_fixed_beacon_fields(struct mac_context *mac_ctx, struct pe_session *sess
 	if (LIM_IS_AP_ROLE(session)) {
 		if (wlan_vdev_mlme_is_mlo_ap(session->vdev)) {
 			lim_update_link_info(mac_ctx, session, bcn_1, bcn_2);
-			populate_dot11f_bcn_mlo_ie(mac_ctx, session,
-						   &bcn_2->mlo_ie);
+			mlo_ie_len = lim_send_bcn_frame_mlo(mac_ctx, session);
 			populate_dot11f_mlo_rnr(
 				mac_ctx, session,
 				&bcn_2->reduced_neighbor_report);
@@ -970,6 +970,16 @@ sch_set_fixed_beacon_fields(struct mac_context *mac_ctx, struct pe_session *sess
 		n_bytes = ie_buf_size + eht_cap_ie_len;
 	}
 
+	if (mlo_ie_len) {
+		status = lim_fill_complete_mlo_ie(session, mlo_ie_len,
+					 session->pSchBeaconFrameEnd + n_bytes);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			pe_debug("assemble ml ie error");
+			mlo_ie_len = 0;
+		}
+		n_bytes += mlo_ie_len;
+	}
+
 	/* Fill the CSA/ECSA count offsets if the IEs are present */
 	mac_ctx->sch.ecsa_count_offset = 0;
 	mac_ctx->sch.csa_count_offset = 0;

+ 356 - 97
core/mac/src/sys/legacy/src/utils/src/parser_api.c

@@ -3597,9 +3597,11 @@ sir_convert_assoc_resp_frame2_mlo_struct(uint8_t *frame, uint32_t frame_len,
 {
 	uint8_t *ml_ie;
 	qdf_size_t ml_ie_total_len;
-	tDot11fIEmlo_ie *ml_ie_info;
+	struct wlan_mlo_ie *ml_ie_info;
 	bool link_id_found;
 	uint8_t link_id;
+	bool eml_cap_found;
+	uint16_t eml_cap;
 	struct qdf_mac_addr mld_mac_addr;
 	QDF_STATUS status = QDF_STATUS_SUCCESS;
 
@@ -3609,7 +3611,6 @@ sir_convert_assoc_resp_frame2_mlo_struct(uint8_t *frame, uint32_t frame_len,
 					&ml_ie, &ml_ie_total_len);
 		if (QDF_IS_STATUS_SUCCESS(status)) {
 			ml_ie_info = &p_assoc_rsp->mlo_ie.mlo_ie;
-			ml_ie_info->present = true;
 			util_get_bvmlie_persta_partner_info(ml_ie,
 					       ml_ie_total_len,
 					       &session_entry->ml_partner_info);
@@ -3625,11 +3626,27 @@ sir_convert_assoc_resp_frame2_mlo_struct(uint8_t *frame, uint32_t frame_len,
 			util_get_bvmlie_primary_linkid(ml_ie, ml_ie_total_len,
 						       &link_id_found,
 						       &link_id);
+			util_get_bvmlie_eml_cap(ml_ie, ml_ie_total_len,
+						&eml_cap_found, &eml_cap);
+			if (eml_cap_found) {
+				ml_ie_info->eml_capab_present = eml_cap_found;
+				ml_ie_info->eml_capabilities_info.emlsr_support =
+				  QDF_GET_BITS(eml_cap,
+				     WLAN_ML_BV_CINFO_EMLCAP_EMLSRSUPPORT_IDX,
+				     WLAN_ML_BV_CINFO_EMLCAP_EMLSRSUPPORT_BITS);
+				ml_ie_info->eml_capabilities_info.transition_timeout =
+				  QDF_GET_BITS(eml_cap,
+				     WLAN_ML_BV_CINFO_EMLCAP_TRANSTIMEOUT_IDX,
+				     WLAN_ML_BV_CINFO_EMLCAP_TRANSTIMEOUT_BITS);
+			}
+
+			ml_ie_info->num_sta_profile =
+			       session_entry->ml_partner_info.num_partner_links;
 			ml_ie_info->link_id_info_present = link_id_found;
-			ml_ie_info->link_id_info.info.link_id = link_id;
+			ml_ie_info->link_id = link_id;
 			pe_debug("Partner link count: %d, Link id: %d, MLD mac addr: " QDF_MAC_ADDR_FMT,
 				 ml_ie_info->num_sta_profile,
-				 ml_ie_info->link_id_info.info.link_id,
+				 ml_ie_info->link_id,
 				 QDF_MAC_ADDR_REF(ml_ie_info->mld_mac_addr));
 		}
 	}
@@ -3931,12 +3948,6 @@ sir_convert_assoc_resp_frame2_struct(struct mac_context *mac,
 	fils_convert_assoc_rsp_frame2_struct(ar, pAssocRsp);
 	sir_convert_assoc_resp_frame2_mlo_struct(frame, frame_len,
 						 session_entry, ar, pAssocRsp);
-	if (ar->mlo_ie.eml_capab_present) {
-		pAssocRsp->mlo_ie.mlo_ie.eml_capabilities.info.emlsr_support =
-			ar->mlo_ie.eml_capabilities.info.emlsr_support;
-		pAssocRsp->mlo_ie.mlo_ie.eml_capabilities.info.transition_timeout =
-			ar->mlo_ie.eml_capabilities.info.transition_timeout;
-	}
 	pe_debug("ht %d vht %d vendor vht: cap %d op %d, he %d he 6ghband %d eht %d eht320 %d, max idle: present %d val %d, he mu edca %d wmm %d qos %d",
 		 ar->HTCaps.present, ar->VHTCaps.present,
 		 ar->vendor_vht_ie.VHTCaps.present,
@@ -4861,6 +4872,51 @@ static inline void convert_bcon_bss_color_change_ie(tDot11fBeacon *bcn_frm,
 {}
 #endif
 
+#ifdef WLAN_FEATURE_11BE_MLO
+static QDF_STATUS
+sir_convert_beacon_frame2_mlo_struct(uint8_t *pframe, uint32_t nframe,
+				     tDot11fBeacon *bcn_frm,
+				     tpSirProbeRespBeacon bcn_struct)
+{
+	uint8_t *ml_ie, *sta_prof;
+	qdf_size_t ml_ie_total_len;
+	uint8_t common_info_len = 0;
+	struct mlo_partner_info partner_info;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	if (bcn_frm->mlo_ie.present) {
+		status = util_find_mlie(pframe + WLAN_BEACON_IES_OFFSET,
+					nframe - WLAN_BEACON_IES_OFFSET,
+					&ml_ie, &ml_ie_total_len);
+		if (QDF_IS_STATUS_SUCCESS(status)) {
+			util_get_bvmlie_persta_partner_info(ml_ie,
+							    ml_ie_total_len,
+							    &partner_info);
+			bcn_struct->mlo_ie.mlo_ie.num_sta_profile =
+						partner_info.num_partner_links;
+			util_get_mlie_common_info_len(ml_ie, ml_ie_total_len,
+						      &common_info_len);
+			sta_prof = ml_ie + sizeof(struct wlan_ie_multilink) +
+				   common_info_len;
+
+			lim_store_mlo_ie_raw_info(ml_ie, sta_prof,
+						  ml_ie_total_len,
+						  &bcn_struct->mlo_ie.mlo_ie);
+		}
+	}
+
+	return status;
+}
+#else
+static inline QDF_STATUS
+sir_convert_beacon_frame2_mlo_struct(uint8_t *pframe, uint32_t nframe,
+				     tDot11fBeacon *bcn_frm,
+				     tpSirProbeRespBeacon bcn_struct)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif
+
 QDF_STATUS
 sir_convert_beacon_frame2_struct(struct mac_context *mac,
 				 uint8_t *pFrame,
@@ -5245,6 +5301,8 @@ sir_convert_beacon_frame2_struct(struct mac_context *mac,
 	}
 
 	convert_bcon_bss_color_change_ie(pBeacon, pBeaconStruct);
+	sir_convert_beacon_frame2_mlo_struct(pPayload, nPayload, pBeacon,
+					     pBeaconStruct);
 
 	qdf_mem_free(pBeacon);
 	return QDF_STATUS_SUCCESS;
@@ -8742,10 +8800,10 @@ QDF_STATUS populate_dot11f_assoc_rsp_mlo_ie(struct mac_context *mac_ctx,
 {
 	int link;
 	int num_sta_pro = 0;
-	tDot11fIEsta_profile *sta_pro;
+	struct wlan_mlo_ie *mlo_ie;
+	struct wlan_mlo_sta_profile *sta_pro;
 	struct mlo_link_ie_info *link_info;
 	struct mlo_link_ie *link_ie;
-	tDot11fIEmlo_ie *mlo_ie = &frm->mlo_ie;
 	tpSirAssocReq assoc_req;
 	tpSirAssocReq link_assoc_req;
 	const uint8_t *reported_p2p_ie;
@@ -8771,10 +8829,28 @@ QDF_STATUS populate_dot11f_assoc_rsp_mlo_ie(struct mac_context *mac_ctx,
 	tDot11fIEP2PAssocRes sta_p2p_assoc_res;
 	tDot11fIEnon_inheritance sta_non_inheritance;
 	uint8_t common_info_len = 0, len = 0;
+	uint8_t *p_ml_ie;
+	uint16_t len_remaining;
+	uint16_t presence_bitmap = 0;
+	QDF_STATUS status;
 
-	mlo_ie->present = 1;
-	mlo_ie->type = 0;
+	if (!mac_ctx || !session || !frm)
+		return QDF_STATUS_E_NULL_VALUE;
+
+	mlo_ie = &session->mlo_ie;
 
+	p_ml_ie = mlo_ie->data;
+	len_remaining = sizeof(mlo_ie->data);
+
+	*p_ml_ie++ = WLAN_ELEMID_EXTN_ELEM;
+	len_remaining--;
+	/* set length later */
+	*p_ml_ie++ = 0;
+	len_remaining--;
+	*p_ml_ie++ = WLAN_EXTN_ELEMID_MULTI_LINK;
+	len_remaining--;
+
+	mlo_ie->type = 0;
 	/* Common Info Length*/
 	common_info_len += WLAN_ML_BV_CINFO_LENGTH_SIZE;
 	qdf_mem_copy(mlo_ie->mld_mac_addr,
@@ -8783,19 +8859,44 @@ QDF_STATUS populate_dot11f_assoc_rsp_mlo_ie(struct mac_context *mac_ctx,
 	common_info_len += QDF_MAC_ADDR_SIZE;
 
 	mlo_ie->link_id_info_present = 1;
-	mlo_ie->link_id_info.info.link_id = wlan_vdev_get_link_id(
-							session->vdev);
+	presence_bitmap |= WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P;
+	mlo_ie->link_id = wlan_vdev_get_link_id(session->vdev);
 	common_info_len += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
 
 	mlo_ie->bss_param_change_cnt_present = 1;
-	mlo_ie->bss_param_change_cnt.info.bss_param_change_count =
-		session->mlo_link_info.link_ie.bss_param_change_cnt;
+	presence_bitmap |= WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P;
+	mlo_ie->bss_param_change_count =
+			session->mlo_link_info.link_ie.bss_param_change_cnt;
 	common_info_len += WLAN_ML_BV_CINFO_BSSPARAMCHNGCNT_SIZE;
 
 	mlo_ie->mld_capab_present = 0;
 
 	mlo_ie->common_info_length = common_info_len;
 
+	QDF_SET_BITS(*(uint16_t *)p_ml_ie, WLAN_ML_CTRL_TYPE_IDX,
+		     WLAN_ML_CTRL_TYPE_BITS, mlo_ie->type);
+	QDF_SET_BITS(*(uint16_t *)p_ml_ie, WLAN_ML_CTRL_PBM_IDX,
+		     WLAN_ML_CTRL_PBM_BITS, presence_bitmap);
+	p_ml_ie += WLAN_ML_CTRL_SIZE;
+	len_remaining -= WLAN_ML_CTRL_SIZE;
+
+	*p_ml_ie++ = common_info_len;
+	len_remaining--;
+
+	qdf_mem_copy(p_ml_ie, mlo_ie->mld_mac_addr, QDF_MAC_ADDR_SIZE);
+	p_ml_ie += QDF_MAC_ADDR_SIZE;
+	len_remaining -= QDF_MAC_ADDR_SIZE;
+
+	QDF_SET_BITS(*p_ml_ie, WLAN_ML_BV_CINFO_LINKIDINFO_LINKID_IDX,
+		     WLAN_ML_BV_CINFO_LINKIDINFO_LINKID_BITS, mlo_ie->link_id);
+	p_ml_ie++;
+	len_remaining--;
+
+	*p_ml_ie++ = mlo_ie->bss_param_change_count;
+	len_remaining--;
+
+	mlo_ie->num_data = p_ml_ie - mlo_ie->data;
+
 	assoc_req = session->parsedAssocReq[sta->assocId];
 	for (link = 0; link < assoc_req->mlo_info.num_partner_links; link++) {
 		lle_mode = 0;
@@ -8820,31 +8921,36 @@ QDF_STATUS populate_dot11f_assoc_rsp_mlo_ie(struct mac_context *mac_ctx,
 		}
 		link_assoc_req =
 			link_session->parsedAssocReq[link_sta->assocId];
-		sta_pro->present = 1;
 		sta_data = sta_pro->data;
 		sta_len_left = sizeof(sta_pro->data);
+
+		*sta_data++ = WLAN_ML_BV_LINFO_SUBELEMID_PERSTAPROFILE;
+		/* set length later */
+		*sta_data++ = 0;
+		sta_len_left -= 2;
+
 		QDF_SET_BITS(
-			*(uint16_t *)sta_pro->data,
+			*(uint16_t *)sta_data,
 			WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
 			WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS,
 			link_id);
 		QDF_SET_BITS(
-			*(uint16_t *)sta_pro->data,
+			*(uint16_t *)sta_data,
 			WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_IDX,
 			WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_BITS,
 			1);
 		QDF_SET_BITS(
-			*(uint16_t *)sta_pro->data,
+			*(uint16_t *)sta_data,
 			WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_MACADDRP_IDX,
 			WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_MACADDRP_BITS,
 			1);
 		QDF_SET_BITS(
-			*(uint16_t *)sta_pro->data,
+			*(uint16_t *)sta_data,
 			WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BCNINTP_IDX,
 			WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BCNINTP_BITS,
 			1);
 		QDF_SET_BITS(
-			*(uint16_t *)sta_pro->data,
+			*(uint16_t *)sta_data,
 			WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_DTIMINFOP_IDX,
 			WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_DTIMINFOP_BITS,
 			1);
@@ -9304,21 +9410,35 @@ QDF_STATUS populate_dot11f_assoc_rsp_mlo_ie(struct mac_context *mac_ctx,
 			sta_len_left -= sta_len_consumed;
 		}
 		sta_pro->num_data = sta_data - sta_pro->data;
+		if (sta_pro->num_data > WLAN_MAX_IE_LEN + MIN_IE_LEN) {
+			sta_pro->data[TAG_LEN_POS] = WLAN_MAX_IE_LEN;
+			status =
+			    lim_add_frag_ie_for_sta_profile(sta_pro->data,
+							    &sta_pro->num_data);
+			if (status != QDF_STATUS_SUCCESS) {
+				pe_debug("add frg ie for sta profile error.");
+				sta_pro->num_data =
+					WLAN_MAX_IE_LEN + MIN_IE_LEN;
+			}
+		} else {
+			sta_pro->data[TAG_LEN_POS] =
+					sta_pro->num_data - MIN_IE_LEN;
+		}
 		lim_mlo_release_vdev_ref(link_session->vdev);
 		num_sta_pro++;
 	}
 	mlo_ie->num_sta_profile = num_sta_pro;
-	mlo_ie->mld_capabilities.info.max_simultaneous_link_num = num_sta_pro;
+	mlo_ie->mld_capabilities_info.max_simultaneous_link_num = num_sta_pro;
 	return QDF_STATUS_SUCCESS;
 }
 
 QDF_STATUS populate_dot11f_bcn_mlo_ie(struct mac_context *mac_ctx,
-				      struct pe_session *session,
-				      tDot11fIEmlo_ie *mlo_ie)
+				      struct pe_session *session)
 {
 	int link;
 	int num_sta_pro = 0;
-	tDot11fIEsta_profile *sta_pro;
+	struct wlan_mlo_ie *mlo_ie;
+	struct wlan_mlo_sta_profile *sta_pro;
 	struct mlo_link_ie *link_ie;
 	uint16_t vdev_count;
 	struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS];
@@ -9330,13 +9450,22 @@ QDF_STATUS populate_dot11f_bcn_mlo_ie(struct mac_context *mac_ctx,
 	uint32_t sta_len_left;
 	uint32_t sta_len_consumed;
 	uint8_t common_info_length = 0;
+	uint8_t *p_ml_ie;
+	uint16_t len_remaining;
+	uint16_t presence_bitmap = 0;
+	bool sta_pro_present;
+	QDF_STATUS status;
+
+	if (!mac_ctx || !session)
+		return QDF_STATUS_E_NULL_VALUE;
+
+	mlo_ie = &session->mlo_ie;
 
 	/* Common Info Length */
 	common_info_length += WLAN_ML_BV_CINFO_LENGTH_SIZE;
 	qdf_mem_zero(&mac_ctx->sch.sch_mlo_partner,
 		     sizeof(mac_ctx->sch.sch_mlo_partner));
 	sch_info = &mac_ctx->sch.sch_mlo_partner;
-	mlo_ie->present = 1;
 	mlo_ie->type = 0;
 	tmp_offset += 1; /* Element ID */
 	tmp_offset += 1; /* length */
@@ -9349,24 +9478,61 @@ QDF_STATUS populate_dot11f_bcn_mlo_ie(struct mac_context *mac_ctx,
 	tmp_offset += 6; /* mld mac addr */
 	common_info_length += QDF_MAC_ADDR_SIZE;
 	mlo_ie->link_id_info_present = 1;
-	mlo_ie->link_id_info.info.link_id = wlan_vdev_get_link_id(
-						session->vdev);
+	presence_bitmap |= WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P;
+	mlo_ie->link_id = wlan_vdev_get_link_id(session->vdev);
 	tmp_offset += 1; /* link id */
 	common_info_length += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
 	mlo_ie->bss_param_change_cnt_present = 1;
-	mlo_ie->bss_param_change_cnt.info.bss_param_change_count =
-		session->mlo_link_info.link_ie.bss_param_change_cnt;
+	presence_bitmap |= WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P;
+	mlo_ie->bss_param_change_count =
+			session->mlo_link_info.link_ie.bss_param_change_cnt;
 	tmp_offset += 1; /* bss parameters change count */
 	common_info_length += WLAN_ML_BV_CINFO_BSSPARAMCHNGCNT_SIZE;
 	mlo_ie->mld_capab_present = 0;
 	sch_info->num_links = 0;
 
 	lim_get_mlo_vdev_list(session, &vdev_count, wlan_vdev_list);
-	mlo_ie->mld_capabilities.info.max_simultaneous_link_num =
+	mlo_ie->mld_capabilities_info.max_simultaneous_link_num =
 							vdev_count - 1;
 
 	mlo_ie->common_info_length = common_info_length;
 	sch_info->mlo_ie_link_info_ofst = tmp_offset;
+
+	p_ml_ie = mlo_ie->data;
+	len_remaining = sizeof(mlo_ie->data);
+
+	*p_ml_ie++ = WLAN_ELEMID_EXTN_ELEM;
+	len_remaining--;
+	*p_ml_ie++ = 0;
+	len_remaining--;
+	*p_ml_ie++ = WLAN_EXTN_ELEMID_MULTI_LINK;
+	len_remaining--;
+
+	QDF_SET_BITS(*(uint16_t *)p_ml_ie, WLAN_ML_CTRL_TYPE_IDX,
+		     WLAN_ML_CTRL_TYPE_BITS, mlo_ie->type);
+	QDF_SET_BITS(*(uint16_t *)p_ml_ie, WLAN_ML_CTRL_PBM_IDX,
+		     WLAN_ML_CTRL_PBM_BITS, presence_bitmap);
+	p_ml_ie += WLAN_ML_CTRL_SIZE;
+	len_remaining -= WLAN_ML_CTRL_SIZE;
+
+	*p_ml_ie++ = common_info_length;
+	len_remaining--;
+
+	qdf_mem_copy(p_ml_ie, mlo_ie->mld_mac_addr, QDF_MAC_ADDR_SIZE);
+	p_ml_ie += QDF_MAC_ADDR_SIZE;
+	len_remaining -= QDF_MAC_ADDR_SIZE;
+
+	QDF_SET_BITS(*p_ml_ie, WLAN_ML_BV_CINFO_LINKIDINFO_LINKID_IDX,
+		     WLAN_ML_BV_CINFO_LINKIDINFO_LINKID_BITS,
+		     mlo_ie->link_id);
+	p_ml_ie++;
+	len_remaining--;
+
+	*p_ml_ie++ = mlo_ie->bss_param_change_count;
+	len_remaining--;
+
+	mlo_ie->num_data = p_ml_ie - mlo_ie->data;
+
 	for (link = 0; link < vdev_count; link++) {
 		if (!wlan_vdev_list[link])
 			continue;
@@ -9390,12 +9556,15 @@ QDF_STATUS populate_dot11f_bcn_mlo_ie(struct mac_context *mac_ctx,
 		tmp_info->vdev_id = wlan_vdev_get_id(wlan_vdev_list[link]);
 		tmp_info->beacon_interval =
 			link_session->beaconParams.beaconInterval;
-		sta_pro->present = 0;
+		sta_pro_present = false;
 		sta_data = sta_pro->data;
 		sta_len_left = sizeof(sta_pro->data);
+		*sta_data++ = WLAN_ML_BV_LINFO_SUBELEMID_PERSTAPROFILE;
+		*sta_data++ = 0;
+		sta_data += 2; /* sta control */
+		sta_len_left -= 4;
 		tmp_offset = 1; /* sub element id */
 		tmp_offset += 1; /* length */
-		sta_data += 2; /* sta control */
 		if (link_ie->link_csa.present) {
 			sta_len_consumed = 0;
 			dot11f_pack_ie_chan_switch_ann(mac_ctx,
@@ -9404,7 +9573,7 @@ QDF_STATUS populate_dot11f_bcn_mlo_ie(struct mac_context *mac_ctx,
 						       &sta_len_consumed);
 			sta_data += sta_len_consumed;
 			sta_len_left -= sta_len_consumed;
-			sta_pro->present = 1;
+			sta_pro_present = true;
 			tmp_info->csa_ext_csa_exist = true;
 		}
 		if (link_ie->link_ecsa.present) {
@@ -9416,7 +9585,7 @@ QDF_STATUS populate_dot11f_bcn_mlo_ie(struct mac_context *mac_ctx,
 							   &sta_len_consumed);
 			sta_data += sta_len_consumed;
 			sta_len_left -= sta_len_consumed;
-			sta_pro->present = 1;
+			sta_pro_present = true;
 			tmp_info->csa_ext_csa_exist = true;
 		}
 		if (link_ie->link_quiet.present) {
@@ -9426,7 +9595,7 @@ QDF_STATUS populate_dot11f_bcn_mlo_ie(struct mac_context *mac_ctx,
 					     &sta_len_consumed);
 			sta_data += sta_len_consumed;
 			sta_len_left -= sta_len_consumed;
-			sta_pro->present = 1;
+			sta_pro_present = true;
 		}
 		if (link_ie->link_swt_time.present) {
 			sta_len_consumed = 0;
@@ -9435,15 +9604,30 @@ QDF_STATUS populate_dot11f_bcn_mlo_ie(struct mac_context *mac_ctx,
 				sta_len_left, &sta_len_consumed);
 			sta_data += sta_len_consumed;
 			sta_len_left -= sta_len_consumed;
-			sta_pro->present = 1;
+			sta_pro_present = true;
 		}
-		if (sta_pro->present) {
+		if (sta_pro_present) {
 			sta_pro->num_data = sta_data - sta_pro->data;
+			if (sta_pro->num_data > WLAN_MAX_IE_LEN + MIN_IE_LEN) {
+				sta_pro->data[TAG_LEN_POS] = WLAN_MAX_IE_LEN;
+				status =
+				  lim_add_frag_ie_for_sta_profile(sta_pro->data,
+							    &sta_pro->num_data);
+				if (status != QDF_STATUS_SUCCESS) {
+					pe_debug("STA profile flag error");
+					sta_pro->num_data =
+						  WLAN_MAX_IE_LEN + MIN_IE_LEN;
+				}
+			} else {
+				sta_pro->data[TAG_LEN_POS] =
+						sta_pro->num_data - MIN_IE_LEN;
+			}
 			QDF_SET_BITS(
-				*(uint16_t *)sta_pro->data,
+				*(uint16_t *)(sta_pro->data + MIN_IE_LEN),
 				WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
 				WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS,
 				wlan_vdev_get_link_id(link_session->vdev));
+
 			num_sta_pro++;
 			tmp_offset += 2; /* sta control */
 			tmp_info->link_info_sta_prof_ofst = tmp_offset;
@@ -9544,32 +9728,14 @@ void populate_dot11f_rnr_tbtt_info_16(struct mac_context *mac_ctx,
 		rnr_session->mlo_link_info.link_ie.bss_param_change_cnt;
 }
 
-QDF_STATUS
-populate_dot11f_probe_req_mlo_ie(struct mac_context *mac_ctx,
-				 struct pe_session *session,
-				 tDot11fIEmlo_ie *mlo_ie)
-{
-	uint8_t *mld_addr;
-
-	mlo_ie->present = 1;
-	mlo_ie->type = 1;
-	mld_addr = wlan_vdev_mlme_get_mldaddr(session->vdev);
-	qdf_mem_copy(&mlo_ie->mld_mac_addr, mld_addr,
-		     sizeof(mlo_ie->mld_mac_addr));
-	mlo_ie->link_id_info_present = 0;
-
-	return QDF_STATUS_SUCCESS;
-}
-
 QDF_STATUS
 populate_dot11f_mlo_caps(struct mac_context *mac_ctx,
 			 struct pe_session *session,
-			 tDot11fIEmlo_ie *mlo_ie)
+			 struct wlan_mlo_ie *mlo_ie)
 {
 	uint8_t *mld_addr;
 	uint8_t common_info_len = 0;
 
-	mlo_ie->present = 1;
 	mlo_ie->type = 0;
 	/* Common Info Length */
 	common_info_len += WLAN_ML_BV_CINFO_LENGTH_SIZE;
@@ -9594,7 +9760,7 @@ populate_dot11f_mlo_caps(struct mac_context *mac_ctx,
 QDF_STATUS
 sir_convert_mlo_probe_rsp_frame2_struct(uint8_t *ml_ie,
 					uint32_t ml_ie_total_len,
-					tpSirMultiLink_IE mlo_ie_ptr)
+					struct sir_multi_link_ie *mlo_ie_ptr)
 {
 	bool link_id_found;
 	uint8_t link_id;
@@ -9608,9 +9774,8 @@ sir_convert_mlo_probe_rsp_frame2_struct(uint8_t *ml_ie,
 	if (!ml_ie_total_len)
 		return QDF_STATUS_E_NULL_VALUE;
 
-	qdf_mem_zero((uint8_t *)mlo_ie_ptr, sizeof(tSirMultiLink_IE));
+	qdf_mem_zero((uint8_t *)mlo_ie_ptr, sizeof(*mlo_ie_ptr));
 
-	mlo_ie_ptr->mlo_ie.present = 1;
 	util_get_mlie_common_info_len(ml_ie, ml_ie_total_len,
 				      &mlo_ie_ptr->mlo_ie.common_info_length);
 
@@ -9621,15 +9786,14 @@ sir_convert_mlo_probe_rsp_frame2_struct(uint8_t *ml_ie,
 	util_get_bvmlie_primary_linkid(ml_ie, ml_ie_total_len,
 				       &link_id_found, &link_id);
 	mlo_ie_ptr->mlo_ie.link_id_info_present = link_id_found;
-	mlo_ie_ptr->mlo_ie.link_id_info.info.link_id = link_id;
+	mlo_ie_ptr->mlo_ie.link_id = link_id;
 
 	util_get_bvmlie_bssparamchangecnt(ml_ie, ml_ie_total_len,
 					  &bss_param_change_cnt_found,
 					  &bss_param_change_cnt);
 	mlo_ie_ptr->mlo_ie.bss_param_change_cnt_present =
 						bss_param_change_cnt_found;
-	mlo_ie_ptr->mlo_ie.bss_param_change_cnt.info.bss_param_change_count =
-						bss_param_change_cnt;
+	mlo_ie_ptr->mlo_ie.bss_param_change_count = bss_param_change_cnt;
 
 	return QDF_STATUS_SUCCESS;
 }
@@ -10526,13 +10690,14 @@ populate_dot11f_mlo_partner_sta_cap(struct mac_context *mac,
 
 QDF_STATUS populate_dot11f_auth_mlo_ie(struct mac_context *mac_ctx,
 				       struct pe_session *pe_session,
-				       tDot11fIEmlo_ie *mlo_ie)
+				       struct wlan_mlo_ie *mlo_ie)
 {
 	struct qdf_mac_addr *mld_addr;
+	uint8_t *p_ml_ie;
+	uint16_t len_remaining;
 
 	pe_debug("Populate Auth MLO IEs");
 
-	mlo_ie->present = 1;
 	mlo_ie->type = 0;
 
 	mlo_ie->common_info_length = WLAN_ML_BV_CINFO_LENGTH_SIZE;
@@ -10549,6 +10714,30 @@ QDF_STATUS populate_dot11f_auth_mlo_ie(struct mac_context *mac_ctx,
 	mlo_ie->eml_capab_present = 0;
 	mlo_ie->mld_capab_present = 0;
 
+	p_ml_ie = mlo_ie->data;
+	len_remaining = sizeof(mlo_ie->data);
+
+	*p_ml_ie++ = WLAN_ELEMID_EXTN_ELEM;
+	len_remaining--;
+	*p_ml_ie++ = 0;
+	len_remaining--;
+	*p_ml_ie++ = WLAN_EXTN_ELEMID_MULTI_LINK;
+	len_remaining--;
+
+	QDF_SET_BITS(*(uint16_t *)p_ml_ie, WLAN_ML_CTRL_TYPE_IDX,
+		     WLAN_ML_CTRL_TYPE_BITS, mlo_ie->type);
+	p_ml_ie += WLAN_ML_CTRL_SIZE;
+	len_remaining -= WLAN_ML_CTRL_SIZE;
+
+	*p_ml_ie++ = mlo_ie->common_info_length;
+	len_remaining--;
+
+	qdf_mem_copy(p_ml_ie, mlo_ie->mld_mac_addr, QDF_MAC_ADDR_SIZE);
+	p_ml_ie += QDF_MAC_ADDR_SIZE;
+	len_remaining -= QDF_MAC_ADDR_SIZE;
+
+	mlo_ie->num_data = p_ml_ie - mlo_ie->data;
+
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -10558,8 +10747,8 @@ QDF_STATUS populate_dot11f_assoc_req_mlo_ie(struct mac_context *mac_ctx,
 {
 	uint8_t link;
 	uint8_t num_sta_prof = 0, total_sta_prof;
-	tDot11fIEmlo_ie *mlo_ie;
-	tDot11fIEsta_profile *sta_prof;
+	struct wlan_mlo_ie *mlo_ie;
+	struct wlan_mlo_sta_profile *sta_prof;
 	struct mlo_link_info *link_info = NULL;
 	struct mlo_partner_info *partner_info;
 	struct qdf_mac_addr *mld_addr;
@@ -10575,8 +10764,10 @@ QDF_STATUS populate_dot11f_assoc_req_mlo_ie(struct mac_context *mac_ctx,
 	uint8_t chan;
 	uint8_t op_class;
 	uint8_t *p_sta_prof;
+	uint8_t *p_ml_ie;
 	uint32_t len_consumed;
-	uint8_t len_remaining, len;
+	uint16_t len_remaining, len;
+	QDF_STATUS status;
 	bool emlsr_cap = false;
 	struct wlan_objmgr_psoc *psoc;
 	tDot11fIEnon_inheritance sta_prof_non_inherit;
@@ -10589,9 +10780,10 @@ QDF_STATUS populate_dot11f_assoc_req_mlo_ie(struct mac_context *mac_ctx,
 	tDot11fIEeht_cap eht_caps;
 	tDot11fIESuppRates supp_rates;
 	tDot11fIEExtSuppRates ext_supp_rates;
+	uint16_t presence_bitmap = 0;
 	bool is_2g;
 
-	if (!frm)
+	if (!mac_ctx || !pe_session || !frm)
 		return QDF_STATUS_E_NULL_VALUE;
 
 	psoc = wlan_vdev_get_psoc(pe_session->vdev);
@@ -10602,29 +10794,29 @@ QDF_STATUS populate_dot11f_assoc_req_mlo_ie(struct mac_context *mac_ctx,
 
 	pe_debug("Populate Assoc req MLO IEs");
 
-	mlo_ie = &frm->mlo_ie;
+	mlo_ie = &pe_session->mlo_ie;
 
-	mlo_ie->present = 1;
 	mlo_ie->type = 0;
 	mlo_ie->common_info_length = WLAN_ML_BV_CINFO_LENGTH_SIZE;
-	mld_addr = (struct qdf_mac_addr *)wlan_vdev_mlme_get_mldaddr(pe_session->vdev);
-	qdf_mem_copy(&mlo_ie->mld_mac_addr,
-		     mld_addr,
-		     QDF_MAC_ADDR_SIZE);
+	mld_addr =
+	    (struct qdf_mac_addr *)wlan_vdev_mlme_get_mldaddr(pe_session->vdev);
+	qdf_mem_copy(&mlo_ie->mld_mac_addr, mld_addr, QDF_MAC_ADDR_SIZE);
 	mlo_ie->common_info_length += QDF_MAC_ADDR_SIZE;
-	mlo_ie->link_id_info_present = 0;
 
+	mlo_ie->link_id_info_present = 0;
 	mlo_ie->bss_param_change_cnt_present = 0;
 	mlo_ie->medium_sync_delay_info_present = 0;
 	mlo_ie->eml_capab_present = 0;
 	mlo_ie->mld_capab_present = 1;
 
 	if (mlo_ie->mld_capab_present) {
-		mlo_ie->mld_capabilities.info.max_simultaneous_link_num = 1;
-		mlo_ie->mld_capabilities.info.srs_support = 0;
-		mlo_ie->mld_capabilities.info.tid_link_map_supported = 0;
-		mlo_ie->mld_capabilities.info.str_freq_separation = 0;
-		mlo_ie->mld_capabilities.info.aar_support = 0;
+		presence_bitmap |= WLAN_ML_BV_CTRL_PBM_MLDCAP_P;
+		mlo_ie->common_info_length += WLAN_ML_BV_CINFO_MLDCAP_SIZE;
+		mlo_ie->mld_capabilities_info.max_simultaneous_link_num = 1;
+		mlo_ie->mld_capabilities_info.srs_support = 0;
+		mlo_ie->mld_capabilities_info.tid_link_map_supported = 0;
+		mlo_ie->mld_capabilities_info.str_freq_separation = 0;
+		mlo_ie->mld_capabilities_info.aar_support = 0;
 	}
 
 	emlsr_cap = policy_mgr_is_hw_emlsr_capable(psoc);
@@ -10632,11 +10824,60 @@ QDF_STATUS populate_dot11f_assoc_req_mlo_ie(struct mac_context *mac_ctx,
 
 	if (emlsr_cap) {
 		mlo_ie->eml_capab_present = 1;
-		mlo_ie->eml_capabilities.info.emlsr_support = 1;
-		mlo_ie->eml_capabilities.info.emlmr_support = 0;
-		mlo_ie->eml_capabilities.info.transition_timeout = 0;
-		mlo_ie->eml_capabilities.info.emlsr_padding_delay = 0;
+		presence_bitmap |= WLAN_ML_BV_CTRL_PBM_EMLCAP_P;
+		mlo_ie->common_info_length += WLAN_ML_BV_CINFO_EMLCAP_SIZE;
+		mlo_ie->eml_capabilities_info.emlsr_support = 1;
+		mlo_ie->eml_capabilities_info.emlmr_support = 0;
+		mlo_ie->eml_capabilities_info.transition_timeout = 0;
+		mlo_ie->eml_capabilities_info.emlsr_padding_delay = 0;
+	}
+
+	p_ml_ie = mlo_ie->data;
+	len_remaining = sizeof(mlo_ie->data);
+
+	/* element ID, length and extension element ID */
+	*p_ml_ie++ = WLAN_ELEMID_EXTN_ELEM;
+	len_remaining--;
+	/* length will set later */
+	*p_ml_ie++ = 0;
+	len_remaining--;
+	*p_ml_ie++ = WLAN_EXTN_ELEMID_MULTI_LINK;
+	len_remaining--;
+
+	QDF_SET_BITS(*(uint16_t *)p_ml_ie, WLAN_ML_CTRL_TYPE_IDX,
+		     WLAN_ML_CTRL_TYPE_BITS, mlo_ie->type);
+	QDF_SET_BITS(*(uint16_t *)p_ml_ie, WLAN_ML_CTRL_PBM_IDX,
+		     WLAN_ML_CTRL_PBM_BITS, presence_bitmap);
+	p_ml_ie += WLAN_ML_CTRL_SIZE;
+	len_remaining -= WLAN_ML_CTRL_SIZE;
+
+	*p_ml_ie++ = mlo_ie->common_info_length;
+	len_remaining--;
+
+	qdf_mem_copy(p_ml_ie, mld_addr, QDF_MAC_ADDR_SIZE);
+	p_ml_ie += QDF_MAC_ADDR_SIZE;
+	len_remaining -= QDF_MAC_ADDR_SIZE;
+
+	if (mlo_ie->eml_capab_present) {
+		QDF_SET_BITS(*(uint16_t *)p_ml_ie,
+		     WLAN_ML_BV_CINFO_EMLCAP_EMLSRSUPPORT_IDX,
+		     WLAN_ML_BV_CINFO_EMLCAP_EMLSRSUPPORT_BITS,
+		     mlo_ie->eml_capabilities_info.emlsr_support);
+		p_ml_ie += WLAN_ML_BV_CINFO_EMLCAP_SIZE;
+		len_remaining -= WLAN_ML_BV_CINFO_EMLCAP_SIZE;
+	}
+
+	if (mlo_ie->mld_capab_present) {
+		QDF_SET_BITS(*(uint16_t *)p_ml_ie,
+		     WLAN_ML_BV_CINFO_MLDCAP_MAXSIMULLINKS_IDX,
+		     WLAN_ML_BV_CINFO_MLDCAP_MAXSIMULLINKS_BITS,
+		     mlo_ie->mld_capabilities_info.max_simultaneous_link_num);
+		p_ml_ie += WLAN_ML_BV_CINFO_MLDCAP_SIZE;
+		len_remaining -= WLAN_ML_BV_CINFO_MLDCAP_SIZE;
 	}
+
+	mlo_ie->num_data = p_ml_ie - mlo_ie->data;
+
 	/* find out number of links from bcn or prb rsp */
 	partner_info = &pe_session->lim_join_req->partner_info;
 	total_sta_prof = partner_info->num_partner_links;
@@ -10661,25 +10902,29 @@ QDF_STATUS populate_dot11f_assoc_req_mlo_ie(struct mac_context *mac_ctx,
 
 		sta_prof = &mlo_ie->sta_profile[num_sta_prof];
 		link_info = &partner_info->partner_link_info[link];
-		sta_prof->present = 1;
 		p_sta_prof = sta_prof->data;
 		len_remaining = sizeof(sta_prof->data);
 
+		/* subelement ID 0, length(sta_prof->num_data - 2) */
+		*p_sta_prof++ = WLAN_ML_BV_LINFO_SUBELEMID_PERSTAPROFILE;
+		*p_sta_prof++ = 0;
+		len_remaining -= 2;
+
 		qdf_mem_zero(non_inher_ie_lists, sizeof(non_inher_ie_lists));
 		qdf_mem_zero(non_inher_ext_ie_lists,
 			     sizeof(non_inher_ext_ie_lists));
 		non_inher_len = 0;
 		non_inher_ext_len = 0;
 
-		QDF_SET_BITS(*(uint16_t *)sta_prof->data,
+		QDF_SET_BITS(*(uint16_t *)(sta_prof->data + MIN_IE_LEN),
 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS,
 			     link_info->link_id);
-		QDF_SET_BITS(*(uint16_t *)sta_prof->data,
+		QDF_SET_BITS(*(uint16_t *)(sta_prof->data + MIN_IE_LEN),
 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_IDX,
 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_BITS,
 			     1);
-		QDF_SET_BITS(*(uint16_t *)sta_prof->data,
+		QDF_SET_BITS(*(uint16_t *)(sta_prof->data + MIN_IE_LEN),
 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_MACADDRP_IDX,
 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_MACADDRP_BITS,
 			     1);
@@ -10709,20 +10954,20 @@ QDF_STATUS populate_dot11f_assoc_req_mlo_ie(struct mac_context *mac_ctx,
 		/* TBD : populate beacon_interval, dtim_info
 		 * nstr_link_pair_present, nstr_bitmap_size
 		 */
-		QDF_SET_BITS(*(uint16_t *)sta_prof->data,
+		QDF_SET_BITS(*(uint16_t *)(sta_prof->data + MIN_IE_LEN),
 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BCNINTP_IDX,
 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BCNINTP_BITS,
 			     0);
-		QDF_SET_BITS(*(uint16_t *)sta_prof->data,
+		QDF_SET_BITS(*(uint16_t *)(sta_prof->data + MIN_IE_LEN),
 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_DTIMINFOP_IDX,
 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_DTIMINFOP_BITS,
 			     0);
 		QDF_SET_BITS(
-			*(uint16_t *)sta_prof->data,
+			*(uint16_t *)(sta_prof->data + MIN_IE_LEN),
 			WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRLINKPRP_IDX,
 			WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRLINKPRP_BITS,
 			0);
-		QDF_SET_BITS(*(uint16_t *)sta_prof->data,
+		QDF_SET_BITS(*(uint16_t *)(sta_prof->data + MIN_IE_LEN),
 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_IDX,
 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_BITS,
 			     0);
@@ -10915,6 +11160,20 @@ QDF_STATUS populate_dot11f_assoc_req_mlo_ie(struct mac_context *mac_ctx,
 			len_remaining -= len_consumed;
 		}
 		sta_prof->num_data = p_sta_prof - sta_prof->data;
+		if (sta_prof->num_data > WLAN_MAX_IE_LEN + MIN_IE_LEN) {
+			sta_prof->data[TAG_LEN_POS] = WLAN_MAX_IE_LEN;
+			status =
+			    lim_add_frag_ie_for_sta_profile(sta_prof->data,
+							&sta_prof->num_data);
+			if (status != QDF_STATUS_SUCCESS) {
+				pe_debug("STA profile frag error");
+				sta_prof->num_data =
+						WLAN_MAX_IE_LEN + MIN_IE_LEN;
+			}
+		} else {
+			sta_prof->data[TAG_LEN_POS] =
+					sta_prof->num_data - MIN_IE_LEN;
+		}
 		num_sta_prof++;
 	}
 	mlo_ie->num_sta_profile = num_sta_prof;