ソースを参照

qcacmn: Utility functions to parse Probe Request Variant of Multi-Link IE

These utility functions help to parse the Probe Request Variant of
Multi-Link IE.
It implements 11BE draft 2.0 spec

Change-Id: I09dcf47ef481278f5c74082002f83d9c8e2155a4
CRs-Fixed: 3213367
Vinay Adella 3 年 前
コミット
0e54add2cb

+ 74 - 0
umac/cmn_services/cmn_defs/inc/wlan_cmn_ieee80211.h

@@ -2269,6 +2269,80 @@ struct wlan_ml_bv_linfo_perstaprof_stainfo_dtiminfo {
 
 /* End of definitions related to Basic variant Multi-Link element. */
 
+/* Definitions related to Probe Req Multi-Link element Link Info field */
+
+/* Probe Req variant Multi-Link element Link Info field contains zero or more
+ * subelements.
+ */
+
+/**
+ *  enum wlan_ml_prv_linfo_subelementid - IDs for subelements in Probe Req
+ *  Multi-Link element Link Info field.
+ *  @WLAN_ML_PRV_LINFO_SUBELEMID_PERSTAPROFILE: Per-STA Profile
+ */
+enum wlan_ml_prv_linfo_subelementid {
+	WLAN_ML_PRV_LINFO_SUBELEMID_PERSTAPROFILE  = 0,
+	WLAN_ML_PRV_LINFO_SUBELEMID_FRAGMENT = 254,
+};
+
+/**
+ * struct wlan_ml_prv_linfo_perstaprof - Fixed fields of Per-STA Profile
+ * subelement in Probe Request variant Multi-Link element Link Info field
+ * @subelem_id: Subelement ID
+ * @subelem_len: Subelement length
+ * @stacontrol: STA Control
+ */
+struct wlan_ml_prv_linfo_perstaprof {
+	uint8_t subelem_id;
+	uint8_t subelem_len;
+	uint16_t stacontrol;
+} qdf_packed;
+
+/* The above fixed fields may be followed by:
+ * STA Profile (variable size)
+ */
+
+/* Size in octets of STA Control field of Per-STA Profile subelement in Basic
+ * variant Multi-Link element Link Info field.
+ */
+#define WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE                   2
+
+/* Definitions for subfields in STA Control field of Per-STA Profile subelement
+ * in Probe Req variant Multi-Link element Link Info field. Any unused bits are
+ * reserved.
+ */
+/* Link ID */
+#define WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX              0
+#define WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS             4
+/* Complete Profile */
+#define WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_IDX           4
+#define WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_BITS          1
+
+/* Definitions for bits in the Presence Bitmap subfield in Probe Req variant
+ * Multi-Link element Control field. Any unused bits are reserved.
+ */
+/* MLD ID Present */
+#define WLAN_ML_PRV_CTRL_PBM_MLDID_P               ((uint16_t)BIT(0))
+/* Size in octets of Common Info Length subfield of Common Info field in
+ * Probe Req variant Multi-Link element.
+ */
+/* Common Info Length  */
+#define WLAN_ML_PRV_CINFO_LENGTH_SIZE                               1
+
+/* Size in octets of MLD ID subfield in Probe Req variant Multi-Link
+ * element Common Info field.
+ */
+#define WLAN_ML_PRV_CINFO_MLDID_SIZE                                1
+
+/* Max value in octets of Common Info Length subfield of Common Info field in
+ * Probe Req variant Multi-Link element
+ */
+#define WLAN_ML_PRV_CINFO_LENGTH_MAX \
+	(WLAN_ML_PRV_CINFO_LENGTH_SIZE + \
+	 WLAN_ML_PRV_CINFO_MLDID_SIZE)
+
+/* End of definitions related to Probe Request variant Multi-Link element. */
+
 /*
  * Definitions related to MLO specific aspects of Reduced Neighbor Report
  * element.

+ 65 - 0
umac/mlo_mgr/inc/utils_mlo.h

@@ -349,6 +349,55 @@ QDF_STATUS
 util_get_bvmlie_persta_partner_info(uint8_t *mlieseq,
 				    qdf_size_t mlieseqlen,
 				    struct mlo_partner_info *partner_info);
+
+/**
+ * util_get_prvmlie_mldid - Get the MLD ID from a given Probe Request
+ * variant Multi-Link element , of the STA that transmits ML Probe Request
+ * with the Multi-Link element
+ *
+ * @mlieseq: Starting address of the Multi-Link element or Multi-Link element
+ * fragment sequence
+ * @mlieseqlen: Total length of the Multi-Link element or Multi-Link element
+ * fragment sequence
+ * @mldidfound: Pointer to the location where a boolean status should be
+ * updated indicating whether the MLD ID was found or not. This should
+ * be ignored by the caller if the function returns error.
+ * @mldid: Pointer to the location where the value of the MLD ID
+ * should be updated. This should be ignored by the caller if the function
+ * returns error, or if the function indicates that the MLD ID was not
+ * found.
+ *
+ * Return: QDF_STATUS_SUCCESS in the case of success, QDF_STATUS value giving
+ * the reason for error in the case of failure
+ */
+QDF_STATUS
+util_get_prvmlie_mldid(uint8_t *mlieseq, qdf_size_t mlieseqlen,
+		       bool *mldidfound, uint8_t *mldid);
+
+/**
+ * util_get_prvmlie_persta_link_id() - Get per-STA probe req link information
+ *
+ * @mlieseq: Starting address of the Multi-Link element or Multi-Link element
+ * fragment sequence
+ * @mlieseqlen: Total length of the Multi-Link element or Multi-Link element
+ * fragment sequence
+ * @probereq_info: Pointer to the location where the probe req link information
+ * should be updated. This should be ignored by the caller if the function
+ * returns error. Note that success will be returned and the number of links in
+ * this structure will be reported as 0, if no Link Info is found, or no per-STA
+ * profile is found.
+ *
+ * Get probe req link information in the per-STA profiles present in a Probe req
+ * variant Multi-Link element.
+ *
+ * Return: QDF_STATUS_SUCCESS in the case of success, QDF_STATUS value giving
+ * the reason for error in the case of failure
+ */
+QDF_STATUS
+util_get_prvmlie_persta_link_id(uint8_t *mlieseq,
+				qdf_size_t mlieseqlen,
+				struct mlo_probereq_info *probereq_info);
+
 #else
 static inline QDF_STATUS
 util_gen_link_assoc_req(uint8_t *frame, qdf_size_t frame_len, bool isreassoc,
@@ -428,5 +477,21 @@ util_get_bvmlie_persta_partner_info(uint8_t *mlieseq,
 {
 	return QDF_STATUS_E_NOSUPPORT;
 }
+
+static inline QDF_STATUS
+util_get_prvmlie_persta_link_id(uint8_t *mlieseq,
+				qdf_size_t mlieseqlen,
+				struct mlo_probereq_info *probereq_info)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
+static inline QDF_STATUS
+util_get_prvmlie_mldid(uint8_t *mlieseq, qdf_size_t mlieseqlen,
+		       bool *mldcapfound, uint8_t *mldcap)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
 #endif /* WLAN_FEATURE_11BE_MLO */
 #endif /* _WLAN_UTILS_MLO_H_ */

+ 11 - 0
umac/mlo_mgr/inc/wlan_mlo_mgr_public_structs.h

@@ -627,6 +627,17 @@ struct mlo_partner_info {
 #endif
 };
 
+/*
+ * struct mlo_probereq_info – mlo probe req link info
+ * @num_links: no. of link info in probe req
+ * @link_id: target link id of APs
+ */
+struct mlo_probereq_info {
+	uint8_t mlid;
+	uint8_t num_links;
+	uint8_t link_id[WLAN_UMAC_MLO_MAX_VDEVS];
+};
+
 /*
  * struct mlo_tgt_link_info – ML target link info
  * @vdev_id: link peer vdev id

+ 557 - 0
umac/mlo_mgr/src/utils_mlo.c

@@ -239,6 +239,109 @@ util_parse_multi_link_ctrl(uint8_t *mlieseqpayload,
 	return QDF_STATUS_SUCCESS;
 }
 
+static QDF_STATUS
+util_parse_prv_multi_link_ctrl(uint8_t *mlieseqpayload,
+			       qdf_size_t mlieseqpayloadlen,
+			       uint8_t **link_info,
+			       qdf_size_t *link_info_len)
+{
+	qdf_size_t parsed_payload_len;
+	uint16_t mlcontrol;
+	uint16_t presence_bm;
+	uint16_t cinfo_len = 0;
+	uint16_t exp_cinfo_len = 0;
+
+	/* This helper returns the location(s) and length(s) of (sub)field(s)
+	 * inferable after parsing the Multi Link element Control field. These
+	 * location(s) and length(s) is/are in reference to the payload section
+	 * of the Multi Link element (after defragmentation, if applicable).
+	 * Here, the payload is the point after the element ID extension of the
+	 * Multi Link element, and includes the payloads of all subsequent
+	 * fragments (if any) but not the headers of those fragments.
+	 *
+	 * Currently, the helper returns the location and length of the Link
+	 * Info field in the Multi Link element sequence. Other (sub)field(s)
+	 * can be added later as required.
+	 */
+
+	if (!mlieseqpayload) {
+		mlo_err("ML seq payload pointer is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!mlieseqpayloadlen) {
+		mlo_err("ML seq payload len is 0");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (mlieseqpayloadlen < WLAN_ML_CTRL_SIZE) {
+		mlo_err_rl("ML seq payload len %zu < ML Control size %u",
+			   mlieseqpayloadlen, WLAN_ML_CTRL_SIZE);
+		return QDF_STATUS_E_PROTO;
+	}
+
+	parsed_payload_len = 0;
+
+	qdf_mem_copy(&mlcontrol, mlieseqpayload, WLAN_ML_CTRL_SIZE);
+	mlcontrol = qdf_le16_to_cpu(mlcontrol);
+	parsed_payload_len += WLAN_ML_CTRL_SIZE;
+
+	presence_bm = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
+				   WLAN_ML_CTRL_PBM_BITS);
+
+	if (mlieseqpayloadlen <
+			(parsed_payload_len + WLAN_ML_PRV_CINFO_LENGTH_SIZE)) {
+		mlo_err_rl("ML seq payload len %zu insufficient for common info length size %u after parsed payload len %zu.",
+			   mlieseqpayloadlen,
+			   WLAN_ML_PRV_CINFO_LENGTH_SIZE,
+			   parsed_payload_len);
+		return QDF_STATUS_E_PROTO;
+	}
+
+	cinfo_len = *(mlieseqpayload + parsed_payload_len);
+	parsed_payload_len += WLAN_ML_PRV_CINFO_LENGTH_SIZE;
+
+	/* Check if MLD ID is present */
+	if (presence_bm & WLAN_ML_PRV_CTRL_PBM_MLDID_P) {
+		if (mlieseqpayloadlen <
+				(parsed_payload_len +
+				 WLAN_ML_PRV_CINFO_MLDID_SIZE)) {
+			mlo_err_rl("ML seq payload len %zu insufficient for MLD ID size %u after parsed payload len %zu.",
+				   mlieseqpayloadlen,
+				   WLAN_ML_PRV_CINFO_MLDID_SIZE,
+				   parsed_payload_len);
+			return QDF_STATUS_E_PROTO;
+		}
+
+		parsed_payload_len += WLAN_ML_PRV_CINFO_MLDID_SIZE;
+	}
+
+	exp_cinfo_len = parsed_payload_len - WLAN_ML_CTRL_SIZE;
+	if (cinfo_len != exp_cinfo_len) {
+		mlo_err_rl("ML seq common info len %u doesn't match with expected common info len %u",
+			   cinfo_len, exp_cinfo_len);
+		return QDF_STATUS_E_PROTO;
+	}
+
+	if (link_info_len) {
+		*link_info_len = mlieseqpayloadlen - parsed_payload_len;
+		mlo_debug("link_info_len:%zu, parsed_payload_len:%zu",
+			  *link_info_len, parsed_payload_len);
+	}
+
+	if (mlieseqpayloadlen == parsed_payload_len) {
+		mlo_debug("No Link Info field present");
+		if (link_info)
+			*link_info = NULL;
+		return QDF_STATUS_SUCCESS;
+	}
+
+	if (link_info)
+		*link_info = mlieseqpayload + parsed_payload_len;
+
+	return QDF_STATUS_SUCCESS;
+}
+
 static QDF_STATUS
 util_parse_bvmlie_perstaprofile_stactrl(uint8_t *subelempayload,
 					qdf_size_t subelempayloadlen,
@@ -481,6 +584,91 @@ util_parse_bvmlie_perstaprofile_stactrl(uint8_t *subelempayload,
 	return QDF_STATUS_SUCCESS;
 }
 
+static QDF_STATUS
+util_parse_prvmlie_perstaprofile_stactrl(uint8_t *subelempayload,
+					 qdf_size_t subelempayloadlen,
+					 uint8_t *linkid,
+					 bool is_staprof_reqd,
+					 uint8_t **staprof,
+					 qdf_size_t *staprof_len)
+{
+	qdf_size_t parsed_payload_len = 0;
+	uint16_t stacontrol;
+	uint8_t completeprofile;
+
+	/* This helper returns the location(s) and where required, the length(s)
+	 * of (sub)field(s) inferable after parsing the STA Control field in the
+	 * per-STA profile subelement. These location(s) and length(s) is/are in
+	 * reference to the payload section of the per-STA profile subelement
+	 * (after defragmentation, if applicable).  Here, the payload is the
+	 * point after the subelement length in the subelement, and includes the
+	 * payloads of all subsequent fragments (if any) but not the headers of
+	 * those fragments.
+	 *
+	 * Currently, the helper returns the link ID, MAC address, and STA
+	 * profile. More (sub)fields can be added when required.
+	 */
+
+	if (!subelempayload) {
+		mlo_err("Pointer to subelement payload is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!subelempayloadlen) {
+		mlo_err("Length of subelement payload is zero");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (subelempayloadlen < WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE) {
+		mlo_err_rl("Subelement payload length %zu octets is smaller than STA control field of per-STA profile subelement %u octets",
+			   subelempayloadlen,
+			   WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE);
+		return QDF_STATUS_E_PROTO;
+	}
+
+	parsed_payload_len = 0;
+
+	qdf_mem_copy(&stacontrol,
+		     subelempayload,
+		     WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE);
+	stacontrol = qdf_le16_to_cpu(stacontrol);
+	parsed_payload_len += WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE;
+
+	if (linkid) {
+		*linkid = QDF_GET_BITS(stacontrol,
+				       WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
+				       WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS);
+	}
+
+	/* Check if this a complete profile */
+	completeprofile = QDF_GET_BITS(stacontrol,
+				       WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_IDX,
+				       WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_BITS);
+
+	/* Note: Some implementation versions of hostapd/wpa_supplicant may
+	 * provide a per-STA profile without STA profile. Let the caller
+	 * indicate whether a STA profile is required to be found. This may be
+	 * revisited as upstreaming progresses.
+	 */
+	if (!is_staprof_reqd)
+		return QDF_STATUS_SUCCESS;
+
+	if (subelempayloadlen == parsed_payload_len) {
+		mlo_err_rl("Subelement payload length %zu == parsed payload length %zu. Unable to get STA profile.",
+			   subelempayloadlen,
+			   parsed_payload_len);
+		return QDF_STATUS_E_PROTO;
+	}
+
+	if (staprof_len)
+		*staprof_len = subelempayloadlen - parsed_payload_len;
+
+	if (staprof)
+		*staprof = subelempayload + parsed_payload_len;
+
+	return QDF_STATUS_SUCCESS;
+}
+
 static
 uint8_t *util_get_successorfrag(uint8_t *currie, uint8_t *frame, qdf_size_t len)
 {
@@ -677,6 +865,156 @@ QDF_STATUS util_parse_partner_info_from_linkinfo(uint8_t *linkinfo,
 	return QDF_STATUS_SUCCESS;
 }
 
+static QDF_STATUS
+util_parse_probereq_info_from_linkinfo(uint8_t *linkinfo,
+				       qdf_size_t linkinfo_len,
+				       struct mlo_probereq_info *probereq_info)
+{
+	uint8_t linkid;
+	uint8_t *linkinfo_currpos;
+	qdf_size_t linkinfo_remlen;
+	bool is_subelemfragseq;
+	uint8_t subelemid;
+	qdf_size_t subelemseqtotallen;
+	qdf_size_t subelemseqpayloadlen;
+	qdf_size_t defragpayload_len;
+	QDF_STATUS ret;
+
+	/* This helper function parses probe request info from the per-STA prof
+	 * present (if any) in the Link Info field in the payload of a Multi
+	 * Link element (after defragmentation if required). The caller should
+	 * pass a copy of the payload so that inline defragmentation of
+	 * subelements can be carried out if required. The subelement
+	 * defragmentation (if applicable) in this Control Path helper is
+	 * required for maintainability, accuracy and eliminating current and
+	 * future per-field-access multi-level fragment boundary checks and
+	 * adjustments, given the complex format of Multi Link elements. It is
+	 * also most likely to be required mainly at the client side.
+	 */
+
+	if (!linkinfo) {
+		mlo_err("linkinfo is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!linkinfo_len) {
+		mlo_err("linkinfo_len is zero");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!probereq_info) {
+		mlo_err("ML probe req info is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	probereq_info->num_links = 0;
+	linkinfo_currpos = linkinfo;
+	linkinfo_remlen = linkinfo_len;
+
+	while (linkinfo_remlen) {
+		if (linkinfo_remlen <  sizeof(struct subelem_header)) {
+			mlo_err_rl("Remaining length in link info %zu octets is smaller than subelement header length %zu octets",
+				   linkinfo_remlen,
+				   sizeof(struct subelem_header));
+			return QDF_STATUS_E_PROTO;
+		}
+
+		subelemid = linkinfo_currpos[ID_POS];
+		is_subelemfragseq = false;
+		subelemseqtotallen = 0;
+		subelemseqpayloadlen = 0;
+
+		ret = wlan_get_subelem_fragseq_info(WLAN_ML_PRV_LINFO_SUBELEMID_FRAGMENT,
+						    linkinfo_currpos,
+						    linkinfo_remlen,
+						    &is_subelemfragseq,
+						    &subelemseqtotallen,
+						    &subelemseqpayloadlen);
+		if (QDF_IS_STATUS_ERROR(ret))
+			return ret;
+
+		if (is_subelemfragseq) {
+			if (!subelemseqpayloadlen) {
+				mlo_err_rl("Subelement fragment sequence payload is reported as 0, investigate");
+				return QDF_STATUS_E_FAILURE;
+			}
+
+			mlo_debug("Subelement fragment sequence found with payload len %zu",
+				  subelemseqpayloadlen);
+
+			ret = wlan_defrag_subelem_fragseq(true,
+							  WLAN_ML_PRV_LINFO_SUBELEMID_FRAGMENT,
+							  linkinfo_currpos,
+							  linkinfo_remlen,
+							  NULL,
+							  0,
+							  &defragpayload_len);
+			if (QDF_IS_STATUS_ERROR(ret))
+				return ret;
+
+			if (defragpayload_len != subelemseqpayloadlen) {
+				mlo_err_rl("Length of defragmented payload %zu octets is not equal to length of subelement fragment sequence payload %zu octets",
+					   defragpayload_len,
+					   subelemseqpayloadlen);
+				return QDF_STATUS_E_FAILURE;
+			}
+
+			/* Adjust linkinfo_remlen to reflect removal of all
+			 * subelement headers except the header of the lead
+			 * subelement.
+			 */
+			linkinfo_remlen -= (subelemseqtotallen -
+					    subelemseqpayloadlen -
+					    sizeof(struct subelem_header));
+		} else {
+			if (linkinfo_remlen <
+				(sizeof(struct subelem_header) +
+				 linkinfo_currpos[TAG_LEN_POS])) {
+				mlo_err_rl("Remaining length in link info %zu octets is smaller than total size of current subelement %zu octets",
+					   linkinfo_remlen,
+					   sizeof(struct subelem_header) +
+					   linkinfo_currpos[TAG_LEN_POS]);
+				return QDF_STATUS_E_PROTO;
+			}
+
+			subelemseqpayloadlen = linkinfo_currpos[TAG_LEN_POS];
+		}
+
+		if (subelemid == WLAN_ML_PRV_LINFO_SUBELEMID_PERSTAPROFILE) {
+			ret = util_parse_prvmlie_perstaprofile_stactrl(linkinfo_currpos +
+								      sizeof(struct subelem_header),
+								      subelemseqpayloadlen,
+								      &linkid,
+								      false,
+								      NULL,
+								      NULL);
+			if (QDF_IS_STATUS_ERROR(ret))
+				return ret;
+
+			if (probereq_info->num_links >=
+				QDF_ARRAY_SIZE(probereq_info->link_id)) {
+				mlo_err_rl("Insufficient size %zu of array for probe req link id",
+					   QDF_ARRAY_SIZE(probereq_info->link_id));
+				return QDF_STATUS_E_NOMEM;
+			}
+
+			probereq_info->link_id[probereq_info->num_links] = linkid;
+
+			probereq_info->num_links++;
+		}
+
+		linkinfo_remlen -= (sizeof(struct subelem_header) +
+				    subelemseqpayloadlen);
+		linkinfo_currpos += (sizeof(struct subelem_header) +
+				     subelemseqpayloadlen);
+	}
+
+	mlo_debug("Number of ML probe request links found=%u",
+		  probereq_info->num_links);
+
+	return QDF_STATUS_SUCCESS;
+}
+
 static
 QDF_STATUS util_get_noninheritlists(uint8_t *buff, qdf_size_t buff_len,
 				    uint8_t **ninherit_elemlist,
@@ -2955,4 +3293,223 @@ util_get_bvmlie_persta_partner_info(uint8_t *mlieseq,
 	return QDF_STATUS_SUCCESS;
 }
 
+QDF_STATUS
+util_get_prvmlie_persta_link_id(uint8_t *mlieseq,
+				qdf_size_t mlieseqlen,
+				struct mlo_probereq_info *probereq_info)
+{
+	struct wlan_ie_multilink *mlie_fixed;
+	uint16_t mlcontrol;
+	enum wlan_ml_variant variant;
+	uint8_t *linkinfo;
+	qdf_size_t linkinfo_len;
+	struct mlo_probereq_info pinfo = {0};
+	qdf_size_t mlieseqpayloadlen;
+	uint8_t *mlieseqpayload_copy;
+	bool is_elemfragseq;
+	qdf_size_t defragpayload_len;
+
+	qdf_size_t tmplen;
+	QDF_STATUS ret;
+
+	if (!mlieseq) {
+		mlo_err("Pointer to Multi-Link element sequence is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!mlieseqlen) {
+		mlo_err("Length of Multi-Link element sequence is zero");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (!probereq_info) {
+		mlo_err("probe request_info is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	probereq_info->num_links = 0;
+
+	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
+		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
+			   mlieseqlen, sizeof(struct wlan_ie_multilink));
+		return QDF_STATUS_E_INVAL;
+	}
+
+	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
+
+	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
+	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)) {
+		mlo_err("The element is not a Multi-Link element");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
+
+	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
+			       WLAN_ML_CTRL_TYPE_BITS);
+
+	if (variant != WLAN_ML_VARIANT_PROBEREQ) {
+		mlo_err("The variant value %u does not correspond to Probe Request Variant value %u",
+			variant, WLAN_ML_VARIANT_PROBEREQ);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	mlieseqpayloadlen = 0;
+	tmplen = 0;
+	is_elemfragseq = false;
+
+	ret = wlan_get_elem_fragseq_info(mlieseq,
+					 mlieseqlen,
+					 &is_elemfragseq,
+					 &tmplen,
+					 &mlieseqpayloadlen);
+	if (QDF_IS_STATUS_ERROR(ret))
+		return ret;
+
+	if (is_elemfragseq) {
+		if (tmplen != mlieseqlen) {
+			mlo_err_rl("Mismatch in values of element fragment sequence total length. Val per frag info determination: %zu octets, val passed as arg: %zu octets",
+				   tmplen, mlieseqlen);
+			return QDF_STATUS_E_INVAL;
+		}
+
+		if (!mlieseqpayloadlen) {
+			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
+			return QDF_STATUS_E_FAILURE;
+		}
+
+		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
+			  mlieseqpayloadlen);
+	} else {
+		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
+			mlo_err_rl("Expected presence of valid fragment sequence since Multi-Link element sequence length %zu octets is larger than frag threshold of %zu octets, however no valid fragment sequence found",
+				   mlieseqlen,
+				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
+			return QDF_STATUS_E_FAILURE;
+		}
+
+		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
+	}
+
+	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
+
+	if (!mlieseqpayload_copy) {
+		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	if (is_elemfragseq) {
+		ret = wlan_defrag_elem_fragseq(false,
+					       mlieseq,
+					       mlieseqlen,
+					       mlieseqpayload_copy,
+					       mlieseqpayloadlen,
+					       &defragpayload_len);
+		if (QDF_IS_STATUS_ERROR(ret)) {
+			qdf_mem_free(mlieseqpayload_copy);
+			return ret;
+		}
+
+		if (defragpayload_len != mlieseqpayloadlen) {
+			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
+				   defragpayload_len, mlieseqpayloadlen);
+			qdf_mem_free(mlieseqpayload_copy);
+			return QDF_STATUS_E_FAILURE;
+		}
+	} else {
+		qdf_mem_copy(mlieseqpayload_copy,
+			     mlieseq + sizeof(struct ie_header) + 1,
+			     mlieseqpayloadlen);
+	}
+
+	linkinfo = NULL;
+	linkinfo_len = 0;
+	ret = util_parse_prv_multi_link_ctrl(mlieseqpayload_copy,
+					     mlieseqpayloadlen,
+					     &linkinfo,
+					     &linkinfo_len);
+	if (QDF_IS_STATUS_ERROR(ret)) {
+		qdf_mem_free(mlieseqpayload_copy);
+		return ret;
+	}
+
+	/* In case Link Info is absent, the number of links will remain
+	 * zero.
+	 */
+	if (!linkinfo) {
+		qdf_mem_free(mlieseqpayload_copy);
+		return QDF_STATUS_SUCCESS;
+	}
+
+	ret = util_parse_probereq_info_from_linkinfo(linkinfo,
+						     linkinfo_len,
+						     &pinfo);
+
+	if (QDF_IS_STATUS_ERROR(ret)) {
+		qdf_mem_free(mlieseqpayload_copy);
+		return ret;
+	}
+
+	qdf_mem_copy(probereq_info, &pinfo, sizeof(*probereq_info));
+
+	qdf_mem_free(mlieseqpayload_copy);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS
+util_get_prvmlie_mldid(uint8_t *mlieseq, qdf_size_t mlieseqlen,
+		       bool *mldidfound, uint8_t *mldid)
+{
+	struct wlan_ie_multilink *mlie_fixed;
+	enum wlan_ml_variant variant;
+	uint16_t mlcontrol;
+	uint16_t presencebitmap;
+	uint8_t *commoninfo;
+	qdf_size_t commoninfolen;
+
+	if (!mlieseq || !mlieseqlen || !mldidfound || !mldid)
+		return QDF_STATUS_E_NULL_VALUE;
+
+	*mldidfound = false;
+	*mldid = 0;
+
+	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
+		return QDF_STATUS_E_INVAL;
+
+	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
+
+	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
+	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
+		return QDF_STATUS_E_INVAL;
+
+	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
+
+	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
+			       WLAN_ML_CTRL_TYPE_BITS);
+
+	if (variant != WLAN_ML_VARIANT_PROBEREQ)
+		return QDF_STATUS_E_NOSUPPORT;
+
+	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
+				      WLAN_ML_CTRL_PBM_BITS);
+
+	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
+	commoninfolen = WLAN_ML_PRV_CINFO_LENGTH_SIZE;
+
+	if (presencebitmap & WLAN_ML_PRV_CTRL_PBM_MLDID_P) {
+		if ((sizeof(struct wlan_ie_multilink) + commoninfolen +
+		     WLAN_ML_PRV_CINFO_MLDID_SIZE) >
+		    mlieseqlen)
+			return QDF_STATUS_E_PROTO;
+
+		*mldid = *((uint8_t *)(commoninfo + commoninfolen));
+		commoninfolen += WLAN_ML_PRV_CINFO_MLDID_SIZE;
+
+		*mldidfound = true;
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
 #endif