diff --git a/umac/cmn_services/cmn_defs/inc/wlan_cmn_ieee80211.h b/umac/cmn_services/cmn_defs/inc/wlan_cmn_ieee80211.h index 3f76f1922a..3eae4f52aa 100644 --- a/umac/cmn_services/cmn_defs/inc/wlan_cmn_ieee80211.h +++ b/umac/cmn_services/cmn_defs/inc/wlan_cmn_ieee80211.h @@ -1466,6 +1466,65 @@ struct erp_ie { uint8_t value; } qdf_packed; +/** + * struct ac_param_record: AC Parameter Record + * @aci_aifsn: ACI/AIFSN field + * @ecw_min_max: ECWmin/ECWmax field + * @txop_limit: TXOP Limit + */ +struct ac_param_record { + uint8_t aci_aifsn; + uint8_t ecw_min_max; + uint16_t txop_limit; +} qdf_packed; + +/* Max number of access catogeries */ +#define MAX_NUM_AC 4 + +/** + * struct edca_ie: EDCA Parameter Set element + * @ie: EDCA Element id + * @len: EDCA IE length + * @qos_info: QOS information + * @update_edca_info: Update EDCA Info + * @ac_record: AC Parameter Record + */ +struct edca_ie { + uint8_t ie; + uint8_t len; + uint8_t qos_info; + uint8_t update_edca_info; + struct ac_param_record ac_record[MAX_NUM_AC]; +} qdf_packed; + +/** + * struct muac_param_record: MU AC Parameter Record + * @aci_aifsn: ACI/AIFSN field + * @ecw_min_max: ECWmin/ECWmax field + * @mu_edca_timer: MU EDCA Timer + */ +struct muac_param_record { + uint8_t aci_aifsn; + uint8_t ecw_min_max; + uint8_t mu_edca_timer; +} qdf_packed; + +/** + * struct muedca_ie: MU EDCA Parameter Set element + * @elem_id: MU EDCA Element id + * @elem_len: MU EDCA IE length + * @elem_id_extn: MU EDCA extension element id + * @qos_info: QoS Info + * @mu_record: MU AC Parameter Record + */ +struct muedca_ie { + uint8_t elem_id; + uint8_t elem_len; + uint8_t elem_id_extn; + uint8_t qos_info; + struct muac_param_record mu_record[MAX_NUM_AC]; +} qdf_packed; + /** * struct htcap_cmn_ie: HT common IE info * @hc_cap: HT capabilities @@ -2852,6 +2911,67 @@ struct wlan_ml_rv_linfo_perstaprof_stainfo_opparams { * End of definitions related to MLO specific aspects of Reduced Neighbor Report * element. */ + +/* Definitions related to Priority access variant Multi-Link element + * Common Info field + */ + +/* Size in octets of Common Info Length subfield of Common Info field in + * Priority access variant Multi-Link element. + */ +/* Common Info Length */ +#define WLAN_ML_PAV_CINFO_LENGTH_SIZE 1 + +/* Max value in octets of Common Info Length subfield of Common Info field in + * Priority access variant Multi-Link element + */ +#define WLAN_ML_PAV_CINFO_LENGTH_MAX \ + (WLAN_ML_PAV_CINFO_LENGTH_SIZE + \ + QDF_MAC_ADDR_SIZE) + +/** + * struct wlan_ml_pav_linfo_perstaprof - Fixed fields of Per-STA Profile + * subelement in Priority access variant Multi-Link element Link Info field + * @subelem_id: Subelement ID + * @subelem_len: Subelement length + * @stacontrol: STA Control + */ +struct wlan_ml_pav_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 + * Priority access variant Multi-Link element Link Info field. + */ +#define WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_SIZE 2 + +/* Definitions for subfields in STA Control field of Per-STA Profile subelement + * in Priority access variant Multi-Link element Link Info field. Any unused + * bits are reserved. + */ + +/* Link ID */ +#define WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX 0 +#define WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS 4 + +/* End of definitions related to priority access variant Multi-Link element Link + * Info field. + */ + +/* Maximum size of IEs present in sta profile for a link + * EDCA IE and MU EDCA IE are part of this. + */ +#define WLAN_ML_PAV_LINFO_STAPROF_MAXSIZE \ + (sizeof(struct edca_ie) + sizeof(struct muedca_ie)) + +/* End of definitions related to priority access variant Multi-Link element. */ + #endif /* WLAN_FEATURE_11BE_MLO */ #endif /* WLAN_FEATURE_11BE */ diff --git a/umac/mlo_mgr/inc/utils_mlo.h b/umac/mlo_mgr/inc/utils_mlo.h index 9793b0125c..a4c6673de7 100644 --- a/umac/mlo_mgr/inc/utils_mlo.h +++ b/umac/mlo_mgr/inc/utils_mlo.h @@ -25,6 +25,7 @@ #include "wlan_mlo_mgr_public_structs.h" #include #include +#include #ifdef WLAN_FEATURE_11BE_MLO @@ -517,6 +518,24 @@ util_get_rvmlie_persta_link_info(uint8_t *mlieseq, qdf_size_t mlieseqlen, struct ml_rv_info *reconfig_info); +/** + * util_get_pav_mlie_link_info() - Get priority access 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 + * @pa_info: Pointer to the location where the priority access multi link + * information is stored. + * + * Get EPCS priority access information from Priority Access 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_pav_mlie_link_info(uint8_t *mlieseq, + qdf_size_t mlieseqlen, + struct ml_pa_info *pa_info); #else static inline QDF_STATUS util_gen_link_assoc_req(uint8_t *frame, qdf_size_t frame_len, bool isreassoc, @@ -644,5 +663,14 @@ util_get_rvmlie_persta_link_info(uint8_t *mlieseq, { return QDF_STATUS_E_NOSUPPORT; } + +static inline +QDF_STATUS util_get_pav_mlie_link_info(uint8_t *mlieseq, + qdf_size_t mlieseqlen, + struct ml_pa_info *pa_info) +{ + return QDF_STATUS_E_NOSUPPORT; +} + #endif /* WLAN_FEATURE_11BE_MLO */ #endif /* _WLAN_UTILS_MLO_H_ */ diff --git a/umac/mlo_mgr/inc/wlan_mlo_epcs.h b/umac/mlo_mgr/inc/wlan_mlo_epcs.h index d169190bde..25d55664b9 100644 --- a/umac/mlo_mgr/inc/wlan_mlo_epcs.h +++ b/umac/mlo_mgr/inc/wlan_mlo_epcs.h @@ -46,16 +46,42 @@ enum wlan_epcs_category { WLAN_EPCS_CATEGORY_INVALID, }; +/** + * struct ml_pa_partner_link_info - Priority Access ML partner information + * @link_id: Link ID + * @edca: EDCA IE + * @muedca: MU EDCA IE + */ +struct ml_pa_partner_link_info { + uint8_t link_id; + struct edca_ie edca; + struct muedca_ie muedca; +}; + +/** + * struct ml_pa_info - priority access ML info + * @mld_mac_addr: MLD mac address + * @num_links: Number of Links + * @link_info: Partner link information + */ +struct ml_pa_info { + struct qdf_mac_addr mld_mac_addr; + uint8_t num_links; + struct ml_pa_partner_link_info link_info[WLAN_UMAC_MLO_MAX_VDEVS]; +}; + /** * struct wlan_epcs_info - EPCS information of frame * @cat: frame category * @dialog_token: dialog token * @status: status + * @pa_info: Priority access ML info */ struct wlan_epcs_info { enum wlan_epcs_category cat; uint8_t dialog_token; uint16_t status; + struct ml_pa_info pa_info; }; /** @@ -109,6 +135,12 @@ struct epcs_frm { }; }; +/* MIN EPCS request frame length */ +#define EPCS_REQ_MIN_LENGTH 3 + +/* MIN EPCS response frame length */ +#define EPCS_RESP_MIN_LENGTH 5 + #define epcs_alert(format, args...) \ QDF_TRACE_FATAL(QDF_MODULE_ID_EPCS, format, ## args) diff --git a/umac/mlo_mgr/src/utils_mlo.c b/umac/mlo_mgr/src/utils_mlo.c index ecc5e1a77c..34cb4aed64 100644 --- a/umac/mlo_mgr/src/utils_mlo.c +++ b/umac/mlo_mgr/src/utils_mlo.c @@ -4683,6 +4683,462 @@ QDF_STATUS util_get_rvmlie_persta_link_info(uint8_t *mlieseq, return QDF_STATUS_SUCCESS; } +static QDF_STATUS +util_parse_pa_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; + + /* 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; + + parsed_payload_len += WLAN_ML_CTRL_SIZE; + + if (mlieseqpayloadlen < + (parsed_payload_len + + WLAN_ML_PAV_CINFO_LENGTH_MAX)) { + mlo_err_rl("ML seq payload len %zu insufficient for MLD cmn size %u after parsed payload len %zu.", + mlieseqpayloadlen, + WLAN_ML_PAV_CINFO_LENGTH_MAX, + parsed_payload_len); + return QDF_STATUS_E_PROTO; + } + + parsed_payload_len += QDF_MAC_ADDR_SIZE + WLAN_ML_PAV_CINFO_LENGTH_SIZE; + + 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_pamlie_perstaprofile_stactrl(uint8_t *subelempayload, + qdf_size_t subelempayloadlen, + struct ml_pa_partner_link_info *pa_link_info) +{ + qdf_size_t parsed_payload_len = 0; + uint16_t stacontrol; + + /* 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 priority access link information + * for all parner links. + */ + 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_PAV_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_PAV_LINFO_PERSTAPROF_STACTRL_SIZE); + return QDF_STATUS_E_PROTO; + } + + parsed_payload_len = 0; + qdf_mem_copy(&stacontrol, + subelempayload, + WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_SIZE); + + stacontrol = qdf_le16_to_cpu(stacontrol); + parsed_payload_len += WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_SIZE; + + subelempayload += WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_SIZE; + + pa_link_info->link_id = + QDF_GET_BITS(stacontrol, + WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX, + WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS); + + if (subelempayloadlen < + (parsed_payload_len + + WLAN_ML_PAV_LINFO_STAPROF_MAXSIZE)) { + mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain edca params of size %zu octets after parsed payload length of %zu octets.", + subelempayloadlen, + WLAN_ML_PAV_LINFO_STAPROF_MAXSIZE, + parsed_payload_len); + return QDF_STATUS_E_PROTO; + } + + qdf_mem_copy(&pa_link_info->edca, subelempayload, + sizeof(struct edca_ie)); + subelempayload += sizeof(struct edca_ie); + parsed_payload_len += sizeof(struct edca_ie); + + qdf_mem_copy(&pa_link_info->muedca, subelempayload, + sizeof(struct muedca_ie)); + subelempayload += sizeof(struct muedca_ie); + parsed_payload_len += sizeof(struct muedca_ie); + + if (parsed_payload_len != subelempayloadlen) + epcs_debug("Error in processing per sta profile of PA ML IE %zu %zu", parsed_payload_len, subelempayloadlen); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +util_parse_pa_info_from_linkinfo(uint8_t *linkinfo, + qdf_size_t linkinfo_len, + struct ml_pa_info *pa_info) +{ + 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 priority access 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. + * Fragmentation is currently unlikely to be required for subelements + * in Reconfiguration variant Multi-Link elements, but it should be + * handled in order to be future ready. + */ + 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 (!pa_info) { + mlo_err("ML pa info is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + pa_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_LINFO_SUBELEMID_FRAGMENT, + linkinfo_currpos, + linkinfo_remlen, + &is_subelemfragseq, + &subelemseqtotallen, + &subelemseqpayloadlen); + if (QDF_IS_STATUS_ERROR(ret)) + return ret; + + if (qdf_unlikely(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_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_LINFO_SUBELEMID_PERSTAPROFILE) { + struct ml_pa_partner_link_info *link_info = + &pa_info->link_info[pa_info->num_links]; + ret = util_parse_pamlie_perstaprofile_stactrl(linkinfo_currpos + + sizeof(struct subelem_header), + subelemseqpayloadlen, + link_info); + if (QDF_IS_STATUS_ERROR(ret)) + return ret; + } + + pa_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", + pa_info->num_links); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +util_get_pav_mlie_link_info(uint8_t *mlieseq, + qdf_size_t mlieseqlen, + struct ml_pa_info *pa_info) +{ + struct wlan_ie_multilink *mlie_fixed; + uint16_t mlcontrol; + enum wlan_ml_variant variant; + uint8_t *linkinfo; + qdf_size_t linkinfo_len; + struct ml_pa_info painfo = {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 (!pa_info) { + mlo_err("pa_info is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + pa_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_PRIORITYACCESS) { + mlo_err("The variant value %u does not correspond to priority access Variant value %u", + variant, WLAN_ML_VARIANT_PRIORITYACCESS); + 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 (qdf_unlikely(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 (qdf_unlikely(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_pa_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; + } + + mlo_debug("Dumping hex of link info after parsing Multi-Link element control"); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_MLO, QDF_TRACE_LEVEL_ERROR, + linkinfo, linkinfo_len); + + ret = util_parse_pa_info_from_linkinfo(linkinfo, linkinfo_len, &painfo); + if (QDF_IS_STATUS_ERROR(ret)) { + qdf_mem_free(mlieseqpayload_copy); + return ret; + } + + qdf_mem_copy(pa_info, &painfo, sizeof(painfo)); + qdf_mem_free(mlieseqpayload_copy); + + return QDF_STATUS_SUCCESS; +} + #endif #ifdef WLAN_FEATURE_11BE diff --git a/umac/mlo_mgr/src/wlan_mlo_epcs.c b/umac/mlo_mgr/src/wlan_mlo_epcs.c index 84b0ae826f..2b691a959c 100644 --- a/umac/mlo_mgr/src/wlan_mlo_epcs.c +++ b/umac/mlo_mgr/src/wlan_mlo_epcs.c @@ -28,6 +28,53 @@ #include #include +/** + * mlo_process_ml_priorityaccess_ie() - API to parse Priority access ML IE + * @ml_ie: Pointer to start of ML IE + * @ml_ie_len: Length of ML IE + * @priority_access_info: pointer to fill multi link priority access information + * + * Return: QDF_STATUS + */ +static QDF_STATUS +mlo_process_ml_priorityaccess_ie(uint8_t *ml_ie, qdf_size_t ml_ie_len, + struct ml_pa_info *priority_access_info) +{ + uint8_t *ml_pa_ie = NULL; + qdf_size_t ml_pa_ie_len = 0; + QDF_STATUS status; + + if (!ml_ie) { + mlo_err("NULL ml_ie"); + return QDF_STATUS_E_INVAL; + } + + if (!priority_access_info) { + mlo_err("NULL priority_access_info"); + return QDF_STATUS_E_INVAL; + } + + status = util_find_mlie_by_variant(ml_ie, + ml_ie_len, + &ml_pa_ie, + &ml_pa_ie_len, + WLAN_ML_VARIANT_PRIORITYACCESS); + + if (QDF_IS_STATUS_ERROR(status) || !ml_pa_ie) { + mlo_debug("ML IE for reconfig variant not found"); + return QDF_STATUS_E_INVAL; + } + epcs_debug("PAV ML IE with length %zu is present", ml_pa_ie_len); + + status = util_get_pav_mlie_link_info(ml_pa_ie, ml_pa_ie_len, + priority_access_info); + if (QDF_IS_STATUS_ERROR(status)) { + mlo_err("Unable to get sta link info from ML PAV IE"); + return QDF_STATUS_E_INVAL; + } + return QDF_STATUS_SUCCESS; +} + /** * wlan_mlo_parse_epcs_request_action_frame() - API to parse EPCS request action * frame. @@ -43,6 +90,9 @@ wlan_mlo_parse_epcs_request_action_frame(struct wlan_epcs_info *epcs, uint32_t frm_len) { struct epcs_frm *epcs_action_frm; + struct ml_pa_info *priority_access_info = &epcs->pa_info; + uint8_t *pa_ie; + uint16_t pa_ie_len; /* * EPCS request action frame @@ -65,7 +115,15 @@ wlan_mlo_parse_epcs_request_action_frame(struct wlan_epcs_info *epcs, epcs_action_frm->protected_eht_action, epcs_action_frm->dialog_token, frm_len); - return QDF_STATUS_SUCCESS; + if (frm_len > EPCS_REQ_MIN_LENGTH) { + pa_ie = (uint8_t *)epcs_action_frm + EPCS_REQ_MIN_LENGTH; + pa_ie_len = frm_len - EPCS_REQ_MIN_LENGTH; + return mlo_process_ml_priorityaccess_ie(pa_ie, + pa_ie_len, + priority_access_info); + } else { + return QDF_STATUS_SUCCESS; + } } /** @@ -83,6 +141,9 @@ wlan_mlo_parse_epcs_response_action_frame(struct wlan_epcs_info *epcs, uint32_t frm_len) { struct epcs_frm *epcs_action_frm; + struct ml_pa_info *priority_access_info = &epcs->pa_info; + uint8_t *pa_ie; + uint16_t pa_ie_len; /* * EPCS response action frame @@ -109,7 +170,15 @@ wlan_mlo_parse_epcs_response_action_frame(struct wlan_epcs_info *epcs, epcs_action_frm->resp.status_code[0], epcs_action_frm->resp.status_code[1], frm_len); - return QDF_STATUS_SUCCESS; + if (frm_len > EPCS_RESP_MIN_LENGTH) { + pa_ie = (uint8_t *)epcs_action_frm + EPCS_RESP_MIN_LENGTH; + pa_ie_len = frm_len - EPCS_RESP_MIN_LENGTH; + return mlo_process_ml_priorityaccess_ie(pa_ie, + pa_ie_len, + priority_access_info); + } else { + return QDF_STATUS_SUCCESS; + } } /**