qcacmn: Add support to get Link info from reconfig ML IE

In the driver, add support to get the Link information from a given
reconfig variant Multi-link element.

Change-Id: Ia23b26ddb4195da21c071ee5f3c198e0492ef952
CRs-Fixed: 3348524
This commit is contained in:
Deeksha Gupta
2022-11-29 18:18:59 +05:30
committed by Madan Koyyalamudi
부모 b23b9551cc
커밋 f1acd4d6d6
3개의 변경된 파일581개의 추가작업 그리고 0개의 파일을 삭제

파일 보기

@@ -485,6 +485,30 @@ QDF_STATUS
util_get_rvmlie_mldmacaddr(uint8_t *mlieseq, qdf_size_t mlieseqlen,
struct qdf_mac_addr *mldmacaddr);
/**
* util_get_rvmlie_persta_link_info() - Get per-STA reconfig 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
* @reconfig_info: Pointer to the location where the reconfig 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 reconfig link information in the per-STA profiles present in a Reconfig
* 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_rvmlie_persta_link_info(uint8_t *mlieseq,
qdf_size_t mlieseqlen,
struct ml_rv_info *reconfig_info);
#else
static inline QDF_STATUS
util_gen_link_assoc_req(uint8_t *frame, qdf_size_t frame_len, bool isreassoc,
@@ -601,5 +625,13 @@ util_get_rvmlie_mldmacaddr(uint8_t *mlieseq, qdf_size_t mlieseqlen,
{
return QDF_STATUS_E_NOSUPPORT;
}
static inline QDF_STATUS
util_get_rvmlie_persta_link_info(uint8_t *mlieseq,
qdf_size_t mlieseqlen,
struct ml_rv_info *reconfig_info)
{
return QDF_STATUS_E_NOSUPPORT;
}
#endif /* WLAN_FEATURE_11BE_MLO */
#endif /* _WLAN_UTILS_MLO_H_ */

파일 보기

@@ -559,6 +559,30 @@ struct mlo_probereq_info {
bool skip_mbssid;
};
/**
* struct ml_rv_partner_link_info: Partner link information of an ML reconfig IE
* @link_id: Link id advertised by the AP
* @is_delete_timer_p: Delete timer is present or not
* @delete_timer: number of TBTTs of the AP
*/
struct ml_rv_partner_link_info {
uint8_t link_id;
uint8_t is_delete_timer_p;
uint16_t delete_timer;
};
/**
* struct ml_rv_info: Reconfig Multi link information of a 11be beacon
* @mld_mac_addr: MLD mac address
* @num_links: Number of links supported by ML AP
* @link_info: Array containing partner links information
*/
struct ml_rv_info {
struct qdf_mac_addr mld_mac_addr;
uint8_t num_links;
struct ml_rv_partner_link_info link_info[WLAN_UMAC_MLO_MAX_VDEVS];
};
/*
* struct mlo_tgt_link_info ML target link info
* @vdev_id: link peer vdev id

파일 보기

@@ -3907,4 +3907,529 @@ QDF_STATUS util_get_rvmlie_mldmacaddr(uint8_t *mlieseq, qdf_size_t mlieseqlen,
return QDF_STATUS_SUCCESS;
}
static QDF_STATUS
util_parse_rv_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;
/* 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);
/* Check if MLD MAC address is present */
if (presence_bm & WLAN_ML_RV_CTRL_PBM_MLDMACADDR_P) {
if (mlieseqpayloadlen <
(parsed_payload_len +
QDF_MAC_ADDR_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 += QDF_MAC_ADDR_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_rvmlie_perstaprofile_stactrl(uint8_t *subelempayload,
qdf_size_t subelempayloadlen,
uint8_t *linkid,
bool *is_macaddr_valid,
struct qdf_mac_addr *macaddr,
bool *is_delete_timer_valid,
uint16_t *delete_timer)
{
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, Delete timer
* 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_RV_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_RV_LINFO_PERSTAPROF_STACTRL_SIZE);
return QDF_STATUS_E_PROTO;
}
parsed_payload_len = 0;
qdf_mem_copy(&stacontrol,
subelempayload,
WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_SIZE);
stacontrol = qdf_le16_to_cpu(stacontrol);
parsed_payload_len += WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_SIZE;
if (linkid)
*linkid = QDF_GET_BITS(stacontrol,
WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS);
/* Check if this a complete profile */
completeprofile = QDF_GET_BITS(stacontrol,
WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_IDX,
WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_BITS);
if (is_macaddr_valid)
*is_macaddr_valid = false;
/* Check STA MAC address present bit */
if (QDF_GET_BITS(stacontrol,
WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_MACADDRP_IDX,
WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_MACADDRP_BITS)) {
if (subelempayloadlen <
(parsed_payload_len + QDF_MAC_ADDR_SIZE)) {
mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain MAC address of size %u octets after parsed payload length of %zu octets.",
subelempayloadlen, QDF_MAC_ADDR_SIZE,
parsed_payload_len);
return QDF_STATUS_E_PROTO;
}
if (macaddr) {
qdf_mem_copy(macaddr->bytes,
subelempayload + parsed_payload_len,
QDF_MAC_ADDR_SIZE);
mlo_nofl_debug("Copied MAC address: " QDF_MAC_ADDR_FMT,
subelempayload + parsed_payload_len);
if (is_macaddr_valid)
*is_macaddr_valid = true;
}
parsed_payload_len += QDF_MAC_ADDR_SIZE;
}
/* Check Delete timer present bit */
if (QDF_GET_BITS(stacontrol,
WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_DELTIMERP_IDX,
WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_DELTIMERP_BITS)) {
if (subelempayloadlen <
(parsed_payload_len +
WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_DELTIMER_SIZE)) {
mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain Delete timer of size %u octets after parsed payload length of %zu octets.",
subelempayloadlen,
WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_DELTIMER_SIZE,
parsed_payload_len);
return QDF_STATUS_E_PROTO;
}
if (delete_timer) {
qdf_mem_copy(delete_timer,
subelempayload + parsed_payload_len,
WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_DELTIMER_SIZE);
if (is_delete_timer_valid)
*is_delete_timer_valid = true;
}
parsed_payload_len += WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_DELTIMER_SIZE;
}
return QDF_STATUS_SUCCESS;
}
static QDF_STATUS
util_parse_rv_info_from_linkinfo(uint8_t *linkinfo,
qdf_size_t linkinfo_len,
struct ml_rv_info *reconfig_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;
struct qdf_mac_addr mac_addr;
bool is_macaddr_valid;
bool is_delete_timer_valid;
uint16_t delete_timer;
/* 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.
* 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 (!reconfig_info) {
mlo_err("ML reconfig info is NULL");
return QDF_STATUS_E_NULL_VALUE;
}
reconfig_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) {
is_macaddr_valid = false;
is_delete_timer_valid = false;
ret = util_parse_rvmlie_perstaprofile_stactrl(linkinfo_currpos +
sizeof(struct subelem_header),
subelemseqpayloadlen,
&linkid,
&is_macaddr_valid,
&mac_addr,
&is_delete_timer_valid,
&delete_timer);
if (QDF_IS_STATUS_ERROR(ret))
return ret;
reconfig_info->link_info[reconfig_info->num_links].link_id = linkid;
reconfig_info->link_info[reconfig_info->num_links].is_delete_timer_p = is_delete_timer_valid;
if (is_delete_timer_valid)
reconfig_info->link_info[reconfig_info->num_links].delete_timer = delete_timer;
else
mlo_warn_rl("Delete timer not found in STA Info field of per-STA profile with link ID %u",
linkid);
mlo_debug("Per-STA Profile Link ID: %u Delete timer present: %d Delete timer: %u",
reconfig_info->link_info[reconfig_info->num_links].link_id,
reconfig_info->link_info[reconfig_info->num_links].is_delete_timer_p,
reconfig_info->link_info[reconfig_info->num_links].delete_timer);
reconfig_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",
reconfig_info->num_links);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS util_get_rvmlie_persta_link_info(uint8_t *mlieseq,
qdf_size_t mlieseqlen,
struct ml_rv_info *reconfig_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_rv_info rinfo = {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 (!reconfig_info) {
mlo_err("reconfig_info is NULL");
return QDF_STATUS_E_NULL_VALUE;
}
reconfig_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_RECONFIG) {
mlo_err("The variant value %u does not correspond to Reconfig Variant value %u",
variant, WLAN_ML_VARIANT_RECONFIG);
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_rv_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_rv_info_from_linkinfo(linkinfo, linkinfo_len, &rinfo);
if (QDF_IS_STATUS_ERROR(ret)) {
qdf_mem_free(mlieseqpayload_copy);
return ret;
}
qdf_mem_copy(reconfig_info, &rinfo, sizeof(*reconfig_info));
qdf_mem_free(mlieseqpayload_copy);
return QDF_STATUS_SUCCESS;
}
#endif