|
@@ -66,121 +66,231 @@ uint8_t *util_find_extn_eid(uint8_t eid, uint8_t extn_eid,
|
|
}
|
|
}
|
|
|
|
|
|
static
|
|
static
|
|
-uint8_t *util_parse_multi_link_ctrl_ie(uint8_t *subelement,
|
|
|
|
- qdf_size_t len,
|
|
|
|
- qdf_size_t *link_info_ie_len)
|
|
|
|
|
|
+uint8_t *util_parse_multi_link_ctrl(uint8_t *element,
|
|
|
|
+ qdf_size_t len,
|
|
|
|
+ qdf_size_t *link_info_len)
|
|
{
|
|
{
|
|
- qdf_size_t sub_ie_len = 0;
|
|
|
|
|
|
+ qdf_size_t parsed_ie_len = 0;
|
|
|
|
+ struct wlan_ie_multilink *mlie_fixed;
|
|
|
|
+ uint16_t mlcontrol;
|
|
|
|
+ uint16_t presencebm;
|
|
|
|
+
|
|
|
|
+ if (!element) {
|
|
|
|
+ mlo_err("Pointer to element is NULL");
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
|
|
- if (!subelement || !len)
|
|
|
|
|
|
+ if (!len) {
|
|
|
|
+ mlo_err("Length is zero");
|
|
return NULL;
|
|
return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (len < sizeof(struct wlan_ie_multilink)) {
|
|
|
|
+ mlo_err_rl("Length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
|
|
|
|
+ len, sizeof(struct wlan_ie_multilink));
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mlie_fixed = (struct wlan_ie_multilink *)element;
|
|
|
|
+ mlcontrol = le16toh(mlie_fixed->mlcontrol);
|
|
|
|
+ presencebm = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
|
|
|
|
+ WLAN_ML_CTRL_PBM_BITS);
|
|
|
|
+
|
|
|
|
+ parsed_ie_len += sizeof(*mlie_fixed);
|
|
|
|
+
|
|
|
|
+ /* Check if MLD MAC address is present */
|
|
|
|
+ if (presencebm & WLAN_ML_BV_CTRL_PBM_MLDMACADDR_P)
|
|
|
|
+ parsed_ie_len += QDF_MAC_ADDR_SIZE;
|
|
|
|
+
|
|
|
|
+ /* Check if Link ID info is present */
|
|
|
|
+ if (presencebm & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P)
|
|
|
|
+ parsed_ie_len += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
|
|
|
|
+
|
|
|
|
+ /* Check if BSS parameter change count is present */
|
|
|
|
+ if (presencebm & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P)
|
|
|
|
+ parsed_ie_len += WLAN_ML_BV_CINFO_BSSPARAMCHNGCNT_SIZE;
|
|
|
|
+
|
|
|
|
+ /* Check if Medium Sync Delay Info is present */
|
|
|
|
+ if (presencebm & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P)
|
|
|
|
+ parsed_ie_len += WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE;
|
|
|
|
+
|
|
|
|
+ /* Check if EML cap is present */
|
|
|
|
+ if (presencebm & WLAN_ML_BV_CTRL_PBM_EMLCAP_P)
|
|
|
|
+ parsed_ie_len += WLAN_ML_BV_CINFO_EMLCAP_SIZE;
|
|
|
|
|
|
- /* sta prof len = ML IE len + EID extn(1) + Multi Lnk ctrl(2) */
|
|
|
|
- sub_ie_len += TAG_LEN_POS + MIN_IE_LEN + 2;
|
|
|
|
-
|
|
|
|
- /* check if MLD MAC address present */
|
|
|
|
- if (qdf_test_bit(WLAN_ML_CTRL_PBM_IDX |
|
|
|
|
- WLAN_ML_BV_CTRL_PBM_MLDMACADDR_P,
|
|
|
|
- (unsigned long *)&subelement[MULTI_LINK_CTRL_1]))
|
|
|
|
- sub_ie_len = sub_ie_len + QDF_MAC_ADDR_SIZE;
|
|
|
|
-
|
|
|
|
- /* check if Link ID info */
|
|
|
|
- if (qdf_test_bit(WLAN_ML_CTRL_PBM_IDX |
|
|
|
|
- WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P,
|
|
|
|
- (unsigned long *)&subelement[MULTI_LINK_CTRL_1]))
|
|
|
|
- sub_ie_len = sub_ie_len + TAG_LEN_POS;
|
|
|
|
-
|
|
|
|
- /* check if BSS parameter change count */
|
|
|
|
- if (qdf_test_bit(WLAN_ML_CTRL_PBM_IDX |
|
|
|
|
- WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P,
|
|
|
|
- (unsigned long *)&subelement[MULTI_LINK_CTRL_1]))
|
|
|
|
- sub_ie_len = sub_ie_len + TAG_LEN_POS;
|
|
|
|
-
|
|
|
|
- /* check if Medium Sync Delay Info present */
|
|
|
|
- if (qdf_test_bit(WLAN_ML_CTRL_PBM_IDX |
|
|
|
|
- WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P,
|
|
|
|
- (unsigned long *)&subelement[MULTI_LINK_CTRL_1]))
|
|
|
|
- sub_ie_len = sub_ie_len + PAYLOAD_START_POS;
|
|
|
|
-
|
|
|
|
- /* check if EML cap present */
|
|
|
|
- if (qdf_test_bit(WLAN_ML_CTRL_PBM_IDX |
|
|
|
|
- WLAN_ML_BV_CTRL_PBM_EMLCAP_P,
|
|
|
|
- (unsigned long *)&subelement[MULTI_LINK_CTRL_2]))
|
|
|
|
- sub_ie_len = sub_ie_len + SUBELEMENT_START_POS;
|
|
|
|
-
|
|
|
|
- /* check if MLD cap present */
|
|
|
|
- if (qdf_test_bit(WLAN_ML_CTRL_PBM_IDX |
|
|
|
|
- WLAN_ML_BV_CTRL_PBM_MLDCAP_P,
|
|
|
|
- (unsigned long *)&subelement[MULTI_LINK_CTRL_2]))
|
|
|
|
- sub_ie_len = sub_ie_len + PAYLOAD_START_POS;
|
|
|
|
-
|
|
|
|
- if (link_info_ie_len) {
|
|
|
|
- *link_info_ie_len = len - sub_ie_len;
|
|
|
|
- mlo_debug("link_info_ie_len:%zu, sub_ie_len:%zu",
|
|
|
|
- *link_info_ie_len, sub_ie_len);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return &subelement[sub_ie_len];
|
|
|
|
|
|
+ /* Check if MLD cap is present */
|
|
|
|
+ if (presencebm & WLAN_ML_BV_CTRL_PBM_MLDCAP_P)
|
|
|
|
+ parsed_ie_len += WLAN_ML_BV_CINFO_MLDCAP_SIZE;
|
|
|
|
+
|
|
|
|
+ if (link_info_len) {
|
|
|
|
+ *link_info_len = len - parsed_ie_len;
|
|
|
|
+ mlo_debug("link_info_len:%zu, parsed_ie_len:%zu",
|
|
|
|
+ *link_info_len, parsed_ie_len);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return &element[parsed_ie_len];
|
|
}
|
|
}
|
|
|
|
|
|
static
|
|
static
|
|
-uint8_t *util_parse_sta_profile_ie(uint8_t *subelement,
|
|
|
|
- qdf_size_t len,
|
|
|
|
- qdf_size_t *per_sta_prof_ie_len,
|
|
|
|
- struct qdf_mac_addr *bssid)
|
|
|
|
|
|
+uint8_t *util_parse_perstaprofile(uint8_t *subelement,
|
|
|
|
+ qdf_size_t len,
|
|
|
|
+ bool is_staprof_reqd,
|
|
|
|
+ qdf_size_t *staprof_len,
|
|
|
|
+ uint8_t *linkid,
|
|
|
|
+ bool *is_macaddr_valid,
|
|
|
|
+ struct qdf_mac_addr *macaddr)
|
|
{
|
|
{
|
|
- qdf_size_t tmp_len = 0;
|
|
|
|
- uint8_t *tmp = NULL;
|
|
|
|
|
|
+ qdf_size_t subelement_len = 0;
|
|
|
|
+ struct wlan_ml_bv_linfo_perstaprof *perstaprof_fixed;
|
|
|
|
+ uint16_t stacontrol;
|
|
|
|
+ uint8_t completeprofile;
|
|
|
|
+ uint8_t nstrlppresent;
|
|
|
|
+ enum wlan_ml_bv_linfo_perstaprof_stactrl_nstrbmsz nstrbmsz;
|
|
|
|
+
|
|
|
|
+ if (!subelement) {
|
|
|
|
+ mlo_err("Pointer to subelement is NULL");
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
|
|
- if (!subelement || !len)
|
|
|
|
|
|
+ if (!len) {
|
|
|
|
+ mlo_err("Length is zero");
|
|
return NULL;
|
|
return NULL;
|
|
|
|
+ }
|
|
|
|
|
|
- if (subelement[0] == 0)
|
|
|
|
- tmp = subelement;
|
|
|
|
- if (!tmp)
|
|
|
|
|
|
+ if (subelement[0] != WLAN_ML_BV_LINFO_SUBELEMID_PERSTAPROFILE) {
|
|
|
|
+ mlo_err_rl("Pointer to subelement does not point to per-STA profile");
|
|
return NULL;
|
|
return NULL;
|
|
|
|
+ }
|
|
|
|
|
|
- /* tmp_len = 2 (sta ctrl) + 1 (sub EID) + 1 (len) */
|
|
|
|
- tmp_len = PAYLOAD_START_POS + 2;
|
|
|
|
|
|
+ if (len < sizeof(struct wlan_ml_bv_linfo_perstaprof)) {
|
|
|
|
+ mlo_err_rl("len %zu octets is smaller than that required for the fixed portion of per-STA profile (%zu octets)",
|
|
|
|
+ len, sizeof(struct wlan_ml_bv_linfo_perstaprof));
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ perstaprof_fixed = (struct wlan_ml_bv_linfo_perstaprof *)subelement;
|
|
|
|
|
|
- /* check DTIM info present bit */
|
|
|
|
- if (qdf_test_bit(WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_DTIMINFOP_IDX,
|
|
|
|
- (unsigned long *)&subelement[STA_CTRL_1]))
|
|
|
|
- tmp_len = tmp_len + MIN_IE_LEN;
|
|
|
|
|
|
+ subelement_len = sizeof(*perstaprof_fixed);
|
|
|
|
|
|
- /* check Beacon interval present bit */
|
|
|
|
- if (qdf_test_bit(WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BCNINTP_IDX,
|
|
|
|
- (unsigned long *)&subelement[STA_CTRL_1]))
|
|
|
|
- tmp_len = tmp_len + TAG_LEN_POS;
|
|
|
|
|
|
+ stacontrol = le16toh(perstaprof_fixed->stacontrol);
|
|
|
|
|
|
- /* check STA link mac addr info present bit */
|
|
|
|
- if (qdf_test_bit(WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_MACADDRP_IDX,
|
|
|
|
- (unsigned long *)&subelement[STA_CTRL_1])) {
|
|
|
|
- tmp_len = tmp_len + QDF_MAC_ADDR_SIZE;
|
|
|
|
- mlo_debug("copied mac addr: " QDF_MAC_ADDR_FMT, &tmp[4]);
|
|
|
|
- qdf_mem_copy(&bssid->bytes, &tmp[4], QDF_MAC_ADDR_SIZE);
|
|
|
|
|
|
+ if (linkid) {
|
|
|
|
+ *linkid = QDF_GET_BITS(stacontrol,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS);
|
|
}
|
|
}
|
|
|
|
|
|
- /* Add Link ID offset,as it will always be present in assoc rsp mlo ie */
|
|
|
|
- tmp_len = tmp_len + TAG_LEN_POS;
|
|
|
|
|
|
+ /* Check if this a complete profile */
|
|
|
|
+ completeprofile = QDF_GET_BITS(stacontrol,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_IDX,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_BITS);
|
|
|
|
+
|
|
|
|
+ /* We increment the measured length of the per-STA profile by checking
|
|
|
|
+ * for the presence of individual fields. We validate this length
|
|
|
|
+ * against the total length of the sublement only at the end, except in
|
|
|
|
+ * cases where we are actually about to access a given field.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ if (is_macaddr_valid)
|
|
|
|
+ *is_macaddr_valid = false;
|
|
|
|
+
|
|
|
|
+ /* Check STA MAC address present bit */
|
|
|
|
+ if (QDF_GET_BITS(stacontrol,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_MACADDRP_IDX,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_MACADDRP_BITS)) {
|
|
|
|
+ if (macaddr) {
|
|
|
|
+ /* Explicityly check if the length is sufficient to hold
|
|
|
|
+ * the STA MAC address, since we are about to attempt to
|
|
|
|
+ * access the STA MAC address.
|
|
|
|
+ */
|
|
|
|
+ if (len < (subelement_len + QDF_MAC_ADDR_SIZE)) {
|
|
|
|
+ mlo_err_rl("len %zu octets is smaller than min size of per-STA profile required to accommodate STA MAC address (%zu octets)",
|
|
|
|
+ len,
|
|
|
|
+ (subelement_len + QDF_MAC_ADDR_SIZE));
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
|
|
- /* check NTSR Link pair present bit */
|
|
|
|
- if (qdf_test_bit(WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRLINKPRP_IDX % 8,
|
|
|
|
- (unsigned long *)&subelement[STA_CTRL_2]))
|
|
|
|
- tmp_len = tmp_len + MIN_IE_LEN;
|
|
|
|
|
|
+ qdf_mem_copy(macaddr->bytes,
|
|
|
|
+ subelement + subelement_len,
|
|
|
|
+ QDF_MAC_ADDR_SIZE);
|
|
|
|
|
|
- /* check NTSR bitmap size present bit */
|
|
|
|
- if (qdf_test_bit(WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_IDX % 8,
|
|
|
|
- (unsigned long *)&subelement[STA_CTRL_2]))
|
|
|
|
- tmp_len = tmp_len + TAG_LEN_POS;
|
|
|
|
|
|
+ mlo_nofl_debug("Copied MAC address: " QDF_MAC_ADDR_FMT,
|
|
|
|
+ subelement + subelement_len);
|
|
|
|
|
|
- if (len <= tmp_len) {
|
|
|
|
- mlo_err("len %zu <= tmp_len %zu, return", len, tmp_len);
|
|
|
|
|
|
+ if (is_macaddr_valid)
|
|
|
|
+ *is_macaddr_valid = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ subelement_len += QDF_MAC_ADDR_SIZE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Check Beacon Interval present bit */
|
|
|
|
+ if (QDF_GET_BITS(stacontrol,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BCNINTP_IDX,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BCNINTP_BITS))
|
|
|
|
+ subelement_len += WLAN_BEACONINTERVAL_LEN;
|
|
|
|
+
|
|
|
|
+ /* Check DTIM Info present bit */
|
|
|
|
+ if (QDF_GET_BITS(stacontrol,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_DTIMINFOP_IDX,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_DTIMINFOP_BITS))
|
|
|
|
+ subelement_len +=
|
|
|
|
+ sizeof(struct wlan_ml_bv_linfo_perstaprof_stainfo_dtiminfo);
|
|
|
|
+
|
|
|
|
+ /* Check NTSR Link pair present bit */
|
|
|
|
+ nstrlppresent =
|
|
|
|
+ QDF_GET_BITS(stacontrol,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRLINKPRP_IDX,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRLINKPRP_BITS);
|
|
|
|
+
|
|
|
|
+ if (completeprofile && nstrlppresent) {
|
|
|
|
+ /* Check NTSR Bitmap Size bit */
|
|
|
|
+ nstrbmsz =
|
|
|
|
+ QDF_GET_BITS(stacontrol,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_IDX,
|
|
|
|
+ WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_BITS);
|
|
|
|
+
|
|
|
|
+ if (nstrbmsz == WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_1_OCTET) {
|
|
|
|
+ subelement_len += 1;
|
|
|
|
+ } else if (nstrbmsz == WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_2_OCTETS) {
|
|
|
|
+ subelement_len += 2;
|
|
|
|
+ } else {
|
|
|
|
+ /* Though an invalid value cannot occur if only 1 bit is
|
|
|
|
+ * used, we check for it in a generic manner in case the
|
|
|
|
+ * number of bits is increased in the future.
|
|
|
|
+ */
|
|
|
|
+ mlo_err_rl("Invalid NSTR Bitmap size %u", nstrbmsz);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* 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) {
|
|
|
|
+ if (len < subelement_len) {
|
|
|
|
+ mlo_err_rl("len %zu < subelement_len %zu",
|
|
|
|
+ len,
|
|
|
|
+ subelement_len);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return &subelement[subelement_len - 1];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (len <= subelement_len) {
|
|
|
|
+ mlo_err_rl("len %zu <= subelement_len %zu",
|
|
|
|
+ len,
|
|
|
|
+ subelement_len);
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
- *per_sta_prof_ie_len = len - tmp_len;
|
|
|
|
|
|
|
|
- return &tmp[tmp_len];
|
|
|
|
|
|
+ if (staprof_len)
|
|
|
|
+ *staprof_len = len - subelement_len;
|
|
|
|
+
|
|
|
|
+ return &subelement[subelement_len];
|
|
}
|
|
}
|
|
|
|
|
|
static
|
|
static
|
|
@@ -210,6 +320,101 @@ uint8_t *util_get_successorfrag(uint8_t *currie, uint8_t *frame, qdf_size_t len)
|
|
return nextie;
|
|
return nextie;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static
|
|
|
|
+QDF_STATUS util_parse_partner_info_from_linkinfo(uint8_t *linkinfo,
|
|
|
|
+ qdf_size_t linkinfo_len,
|
|
|
|
+ struct mlo_partner_info *partner_info)
|
|
|
|
+{
|
|
|
|
+ uint8_t linkid;
|
|
|
|
+ struct qdf_mac_addr macaddr;
|
|
|
|
+ bool is_macaddr_valid;
|
|
|
|
+ uint8_t *currpos;
|
|
|
|
+ qdf_size_t currlen;
|
|
|
|
+ uint8_t *endofstainfo;
|
|
|
|
+
|
|
|
|
+ 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 (!partner_info) {
|
|
|
|
+ mlo_err("ML partner info is NULL");
|
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ partner_info->num_partner_links = 0;
|
|
|
|
+ currpos = linkinfo;
|
|
|
|
+ currlen = linkinfo_len;
|
|
|
|
+
|
|
|
|
+ while (currlen) {
|
|
|
|
+ if (currlen < MIN_IE_LEN) {
|
|
|
|
+ mlo_err_rl("currlen %zu is smaller than minimum IE length %u",
|
|
|
|
+ currlen, MIN_IE_LEN);
|
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (currlen < (MIN_IE_LEN + currpos[TAG_LEN_POS])) {
|
|
|
|
+ mlo_err_rl("currlen %zu is smaller than length of current IE %u",
|
|
|
|
+ currlen, MIN_IE_LEN + currpos[TAG_LEN_POS]);
|
|
|
|
+ return QDF_STATUS_E_PROTO;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (currpos[ID_POS] ==
|
|
|
|
+ WLAN_ML_BV_LINFO_SUBELEMID_PERSTAPROFILE) {
|
|
|
|
+ is_macaddr_valid = false;
|
|
|
|
+
|
|
|
|
+ /* Per-STA profile fragmentation support may be added
|
|
|
|
+ * once support for this is introduced in the standard.
|
|
|
|
+ */
|
|
|
|
+ endofstainfo = util_parse_perstaprofile(currpos,
|
|
|
|
+ currlen,
|
|
|
|
+ false,
|
|
|
|
+ NULL,
|
|
|
|
+ &linkid,
|
|
|
|
+ &is_macaddr_valid,
|
|
|
|
+ &macaddr);
|
|
|
|
+
|
|
|
|
+ if (!endofstainfo) {
|
|
|
|
+ mlo_err_rl("Error in parsing per-STA profile");
|
|
|
|
+ return QDF_STATUS_E_EMPTY;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (is_macaddr_valid) {
|
|
|
|
+ if (partner_info->num_partner_links >=
|
|
|
|
+ QDF_ARRAY_SIZE(partner_info->partner_link_info)) {
|
|
|
|
+ mlo_err_rl("Insufficient size %zu of array for partner link info",
|
|
|
|
+ QDF_ARRAY_SIZE(partner_info->partner_link_info));
|
|
|
|
+ return QDF_STATUS_E_NOMEM;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ partner_info->partner_link_info[partner_info->num_partner_links].link_id =
|
|
|
|
+ linkid;
|
|
|
|
+ qdf_mem_copy(&partner_info->partner_link_info[partner_info->num_partner_links].link_addr,
|
|
|
|
+ &macaddr,
|
|
|
|
+ sizeof(partner_info->partner_link_info[partner_info->num_partner_links].link_addr));
|
|
|
|
+
|
|
|
|
+ partner_info->num_partner_links++;
|
|
|
|
+ } else {
|
|
|
|
+ mlo_warn_rl("MAC address not found in STA Info field of per-STA profile with link ID %u",
|
|
|
|
+ linkid);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ currlen -= (MIN_IE_LEN + currpos[TAG_LEN_POS]);
|
|
|
|
+ currpos += (MIN_IE_LEN + currpos[TAG_LEN_POS]);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mlo_debug("Number of ML partner links found=%u",
|
|
|
|
+ partner_info->num_partner_links);
|
|
|
|
+
|
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
|
+}
|
|
|
|
+
|
|
QDF_STATUS util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t len,
|
|
QDF_STATUS util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t len,
|
|
struct qdf_mac_addr link_addr,
|
|
struct qdf_mac_addr link_addr,
|
|
uint8_t *assoc_link_frame)
|
|
uint8_t *assoc_link_frame)
|
|
@@ -217,10 +422,11 @@ QDF_STATUS util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t len,
|
|
uint8_t *tmp = NULL;
|
|
uint8_t *tmp = NULL;
|
|
const uint8_t *tmp_old, *rsn_ie;
|
|
const uint8_t *tmp_old, *rsn_ie;
|
|
qdf_size_t sub_len, tmp_rem_len;
|
|
qdf_size_t sub_len, tmp_rem_len;
|
|
- qdf_size_t link_info_len, per_sta_prof_len = 0;
|
|
|
|
|
|
+ qdf_size_t link_info_len, sta_prof_len = 0;
|
|
uint8_t *subelement;
|
|
uint8_t *subelement;
|
|
uint8_t *pos;
|
|
uint8_t *pos;
|
|
uint8_t *sub_copy, *orig_copy;
|
|
uint8_t *sub_copy, *orig_copy;
|
|
|
|
+ bool is_bssid_valid;
|
|
struct qdf_mac_addr bssid;
|
|
struct qdf_mac_addr bssid;
|
|
struct wlan_frame_hdr *hdr;
|
|
struct wlan_frame_hdr *hdr;
|
|
|
|
|
|
@@ -257,9 +463,9 @@ QDF_STATUS util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t len,
|
|
qdf_mem_copy(sub_copy, subelement, sub_len);
|
|
qdf_mem_copy(sub_copy, subelement, sub_len);
|
|
|
|
|
|
/* parse ml ie */
|
|
/* parse ml ie */
|
|
- sub_copy = util_parse_multi_link_ctrl_ie(sub_copy,
|
|
|
|
- sub_len,
|
|
|
|
- &link_info_len);
|
|
|
|
|
|
+ sub_copy = util_parse_multi_link_ctrl(sub_copy,
|
|
|
|
+ sub_len,
|
|
|
|
+ &link_info_len);
|
|
|
|
|
|
if (!sub_copy)
|
|
if (!sub_copy)
|
|
return QDF_STATUS_E_NULL_VALUE;
|
|
return QDF_STATUS_E_NULL_VALUE;
|
|
@@ -268,11 +474,19 @@ QDF_STATUS util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t len,
|
|
QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_MLO, QDF_TRACE_LEVEL_DEBUG,
|
|
QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_MLO, QDF_TRACE_LEVEL_DEBUG,
|
|
sub_copy, link_info_len);
|
|
sub_copy, link_info_len);
|
|
|
|
|
|
- /* parse sta profile ie */
|
|
|
|
- sub_copy = util_parse_sta_profile_ie(sub_copy,
|
|
|
|
- link_info_len,
|
|
|
|
- &per_sta_prof_len,
|
|
|
|
- &bssid);
|
|
|
|
|
|
+ is_bssid_valid = false;
|
|
|
|
+
|
|
|
|
+ /* Parse per-STA profile */
|
|
|
|
+ sub_copy = util_parse_perstaprofile(sub_copy,
|
|
|
|
+ link_info_len,
|
|
|
|
+ true,
|
|
|
|
+ &sta_prof_len,
|
|
|
|
+ NULL,
|
|
|
|
+ &is_bssid_valid,
|
|
|
|
+ &bssid);
|
|
|
|
+
|
|
|
|
+ if (!is_bssid_valid)
|
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
|
|
|
if (!sub_copy) {
|
|
if (!sub_copy) {
|
|
qdf_mem_copy(pos, frame, len);
|
|
qdf_mem_copy(pos, frame, len);
|
|
@@ -289,7 +503,7 @@ QDF_STATUS util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t len,
|
|
while (((tmp_old + tmp_old[TAG_LEN_POS] + MIN_IE_LEN) - frame) <= len) {
|
|
while (((tmp_old + tmp_old[TAG_LEN_POS] + MIN_IE_LEN) - frame) <= len) {
|
|
tmp = (uint8_t *)util_find_eid(tmp_old[0],
|
|
tmp = (uint8_t *)util_find_eid(tmp_old[0],
|
|
sub_copy,
|
|
sub_copy,
|
|
- per_sta_prof_len);
|
|
|
|
|
|
+ sta_prof_len);
|
|
if (!tmp) {
|
|
if (!tmp) {
|
|
/* ie in old ie but not in subelement */
|
|
/* ie in old ie but not in subelement */
|
|
if (tmp_old[2] != WLAN_EXTN_ELEMID_MULTI_LINK) {
|
|
if (tmp_old[2] != WLAN_EXTN_ELEMID_MULTI_LINK) {
|
|
@@ -308,7 +522,7 @@ QDF_STATUS util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t len,
|
|
* vendor ie, compare OUI + type + subType to
|
|
* vendor ie, compare OUI + type + subType to
|
|
* determine if they are the same ie.
|
|
* determine if they are the same ie.
|
|
*/
|
|
*/
|
|
- tmp_rem_len = per_sta_prof_len - (tmp - sub_copy);
|
|
|
|
|
|
+ tmp_rem_len = sta_prof_len - (tmp - sub_copy);
|
|
if (tmp_old[0] == WLAN_ELEMID_VENDOR &&
|
|
if (tmp_old[0] == WLAN_ELEMID_VENDOR &&
|
|
tmp_rem_len >= MIN_VENDOR_TAG_LEN) {
|
|
tmp_rem_len >= MIN_VENDOR_TAG_LEN) {
|
|
if (!qdf_mem_cmp(tmp_old + PAYLOAD_START_POS,
|
|
if (!qdf_mem_cmp(tmp_old + PAYLOAD_START_POS,
|
|
@@ -624,4 +838,96 @@ util_get_bvmlie_primary_linkid(uint8_t *mlieseq, qdf_size_t mlieseqlen,
|
|
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+QDF_STATUS
|
|
|
|
+util_get_bvmlie_persta_partner_info(uint8_t *mlie, qdf_size_t mlielen,
|
|
|
|
+ struct mlo_partner_info *partner_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_partner_info pinfo = {0};
|
|
|
|
+ QDF_STATUS ret;
|
|
|
|
+
|
|
|
|
+ if (!mlie) {
|
|
|
|
+ mlo_err("mlie is NULL");
|
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!mlielen) {
|
|
|
|
+ mlo_err("mlielen is zero");
|
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!partner_info) {
|
|
|
|
+ mlo_err("partner_info is NULL");
|
|
|
|
+ return QDF_STATUS_E_NULL_VALUE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ partner_info->num_partner_links = 0;
|
|
|
|
+
|
|
|
|
+ if (mlielen < sizeof(struct wlan_ie_multilink)) {
|
|
|
|
+ mlo_err_rl("mlielen %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
|
|
|
|
+ mlielen, sizeof(struct wlan_ie_multilink));
|
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Note: Multi-Link element fragmentation support will be added in a
|
|
|
|
+ * later change once shared helper utilities for the same are available.
|
|
|
|
+ * As of now, we temporarily return error if the mlielen is greater than
|
|
|
|
+ * the max length for an IE.
|
|
|
|
+ */
|
|
|
|
+ if (mlielen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
|
|
|
|
+ mlo_err_rl("Element fragmentation is not yet supported for this API");
|
|
|
|
+ return QDF_STATUS_E_NOSUPPORT;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mlie_fixed = (struct wlan_ie_multilink *)mlie;
|
|
|
|
+
|
|
|
|
+ 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 = le16toh(mlie_fixed->mlcontrol);
|
|
|
|
+
|
|
|
|
+ variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
|
|
|
|
+ WLAN_ML_CTRL_TYPE_BITS);
|
|
|
|
+
|
|
|
|
+ if (variant != WLAN_ML_VARIANT_BASIC) {
|
|
|
|
+ mlo_err("The variant value %u does not correspond to Basic Variant value %u",
|
|
|
|
+ variant, WLAN_ML_VARIANT_BASIC);
|
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ linkinfo_len = 0;
|
|
|
|
+ linkinfo = util_parse_multi_link_ctrl(mlie, mlielen,
|
|
|
|
+ &linkinfo_len);
|
|
|
|
+
|
|
|
|
+ if (!linkinfo) {
|
|
|
|
+ mlo_err_rl("Error in parsing Multi-Link element control");
|
|
|
|
+ return QDF_STATUS_E_INVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* In case Link Info is absent as indicated by the Link Info length
|
|
|
|
+ * being 0, return success. The number of partner links will remain 0.
|
|
|
|
+ */
|
|
|
|
+ if (!linkinfo_len) {
|
|
|
|
+ mlo_warn_rl("Link Info is absent");
|
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = util_parse_partner_info_from_linkinfo(linkinfo,
|
|
|
|
+ linkinfo_len,
|
|
|
|
+ &pinfo);
|
|
|
|
+
|
|
|
|
+ if (QDF_IS_STATUS_ERROR(ret))
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ qdf_mem_copy(partner_info, &pinfo, sizeof(*partner_info));
|
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
|
+}
|
|
#endif
|
|
#endif
|