diff --git a/umac/mlme/connection_mgr/core/src/wlan_cm_bss_scoring.c b/umac/mlme/connection_mgr/core/src/wlan_cm_bss_scoring.c index ab22c587e8..fb11a85556 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_bss_scoring.c +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_bss_scoring.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 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 @@ -1728,15 +1728,8 @@ static inline int cm_calculate_emlsr_score(struct weight_cfg *weight_config) return weight_config->emlsr_weightage * mlo_boost_pct[MLSR]; } -/** - * cm_get_entry() - Get bss scan entry by link mac address - * @scan_list: Scan entry list of bss candidates after filtering - * @link_addr: link mac address - * - * Return: Pointer to bss scan entry - */ -static struct scan_cache_entry *cm_get_entry(qdf_list_t *scan_list, - struct qdf_mac_addr *link_addr) +struct scan_cache_entry *cm_get_entry(qdf_list_t *scan_list, + struct qdf_mac_addr *link_addr) { qdf_list_node_t *cur_node = NULL, *next_node = NULL; struct scan_cache_node *curr_entry = NULL; diff --git a/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c b/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c index 539aa3484d..02b0082668 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_connect.c @@ -1974,9 +1974,7 @@ cm_connect_req_update_ml_partner_info(struct cnx_mgr *cm_ctx, &eht_capable); if (!same_candidate_used && eht_capable && cm_bss_peer_is_assoc_peer(conn_req)) { - cm_get_ml_partner_info(pdev, - conn_req->cur_candidate->entry, - &conn_req->req.ml_parnter_info); + cm_get_ml_partner_info(pdev, conn_req); cm_modify_partner_info_based_on_dbs_or_sbs_mode( cm_ctx->vdev, cm_req->cm_id, conn_req->cur_candidate->entry, diff --git a/umac/mlme/connection_mgr/core/src/wlan_cm_main_api.h b/umac/mlme/connection_mgr/core/src/wlan_cm_main_api.h index 05e6e80169..ef9f242b16 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_main_api.h +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_main_api.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2012-2015, 2020-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 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 @@ -1488,6 +1488,19 @@ cm_get_active_connect_req_param(struct wlan_objmgr_vdev *vdev, QDF_STATUS cm_get_rnr(struct wlan_objmgr_vdev *vdev, wlan_cm_id cm_id, struct reduced_neighbor_report *rnr); +/** + * cm_get_curr_candidate_entry() - Get the current candidate from cnx mgr + * @vdev: VDEV object manager. + * @cm_id: cnx mgr ID. + * + * Get current entry of connection from the cnx mgr list. + * Caller to free the returned scan entry if not NULL. + * + * Return: Scan entry + */ +struct scan_cache_entry * +cm_get_curr_candidate_entry(struct wlan_objmgr_vdev *vdev, wlan_cm_id cm_id); + /** * cm_free_connect_rsp_ies() - Function to free all connection IEs. * @connect_rsp: pointer to connect rsp diff --git a/umac/mlme/connection_mgr/core/src/wlan_cm_util.c b/umac/mlme/connection_mgr/core/src/wlan_cm_util.c index c2855444bb..b8be9976c5 100644 --- a/umac/mlme/connection_mgr/core/src/wlan_cm_util.c +++ b/umac/mlme/connection_mgr/core/src/wlan_cm_util.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2012-2015, 2020-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 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 @@ -1845,6 +1845,47 @@ QDF_STATUS cm_get_rnr(struct wlan_objmgr_vdev *vdev, wlan_cm_id cm_id, return QDF_STATUS_E_FAILURE; } +struct scan_cache_entry * +cm_get_curr_candidate_entry(struct wlan_objmgr_vdev *vdev, + wlan_cm_id cm_id) +{ + qdf_list_node_t *cur_node = NULL, *next_node = NULL; + struct cm_req *cm_req; + uint32_t prefix = CM_ID_GET_PREFIX(cm_id); + struct cnx_mgr *cm_ctx; + struct scan_cache_entry *cur_entry, *entry = NULL; + + if (prefix != CONNECT_REQ_PREFIX) + return NULL; + + cm_ctx = cm_get_cm_ctx(vdev); + if (!cm_ctx) + return NULL; + + cm_req_lock_acquire(cm_ctx); + qdf_list_peek_front(&cm_ctx->req_list, &cur_node); + while (cur_node) { + qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node); + cm_req = qdf_container_of(cur_node, struct cm_req, node); + + if (cm_req->cm_id != cm_id) { + cur_node = next_node; + next_node = NULL; + continue; + } + + if (!cm_req->connect_req.cur_candidate) + break; + + cur_entry = cm_req->connect_req.cur_candidate->entry; + entry = util_scan_copy_cache_entry(cur_entry); + break; + } + cm_req_lock_release(cm_ctx); + + return entry; +} + #ifdef WLAN_POLICY_MGR_ENABLE static void cm_get_pcl_chan_weigtage_for_sta(struct wlan_objmgr_pdev *pdev, diff --git a/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_api.h b/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_api.h index 7c1d787874..a90c32f677 100644 --- a/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_api.h +++ b/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_api.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 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 @@ -590,6 +590,19 @@ void wlan_cm_set_candidate_custom_sort_cb( QDF_STATUS wlan_cm_get_rnr(struct wlan_objmgr_vdev *vdev, wlan_cm_id cm_id, struct reduced_neighbor_report *rnr); +/** + * wlan_cm_get_curr_candidate_entry() - Get current candidate from cnx mgr + * @vdev: VDEV object manager + * @cm_id: cnx mgr ID + * + * Get the current candidate for connection from cnx mgr. + * + * Return: Scan entry + */ +struct scan_cache_entry * +wlan_cm_get_curr_candidate_entry(struct wlan_objmgr_vdev *vdev, + wlan_cm_id cm_id); + /** * wlan_cm_disc_cont_after_rso_stop() - Continue disconnect after RSO stop * @vdev: Objmgr vdev diff --git a/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_bss_score_param.h b/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_bss_score_param.h index e379469fd5..427a3a9c22 100644 --- a/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_bss_score_param.h +++ b/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_bss_score_param.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 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 @@ -573,4 +573,14 @@ void wlan_cm_set_check_assoc_disallowed(struct wlan_objmgr_psoc *psoc, void wlan_cm_get_check_assoc_disallowed(struct wlan_objmgr_psoc *psoc, bool *value); #endif + +/** + * cm_get_entry() - Get bss scan entry by link mac address + * @scan_list: Scan entry list of bss candidates after filtering + * @link_addr: link mac address + * + * Return: Pointer to bss scan entry + */ +struct scan_cache_entry *cm_get_entry(qdf_list_t *scan_list, + struct qdf_mac_addr *link_addr); #endif diff --git a/umac/mlme/connection_mgr/dispatcher/src/wlan_cm_api.c b/umac/mlme/connection_mgr/dispatcher/src/wlan_cm_api.c index 765531ad2d..04954e1e99 100644 --- a/umac/mlme/connection_mgr/dispatcher/src/wlan_cm_api.c +++ b/umac/mlme/connection_mgr/dispatcher/src/wlan_cm_api.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2012-2015, 2020-2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 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 @@ -430,6 +430,13 @@ QDF_STATUS wlan_cm_get_rnr(struct wlan_objmgr_vdev *vdev, wlan_cm_id cm_id, return cm_get_rnr(vdev, cm_id, rnr); } +struct scan_cache_entry * +wlan_cm_get_curr_candidate_entry(struct wlan_objmgr_vdev *vdev, + wlan_cm_id cm_id) +{ + return cm_get_curr_candidate_entry(vdev, cm_id); +} + void wlan_cm_connect_resp_fill_mld_addr_from_cm_id(struct wlan_objmgr_vdev *vdev, wlan_cm_id cm_id, diff --git a/umac/mlo_mgr/inc/utils_mlo.h b/umac/mlo_mgr/inc/utils_mlo.h index 2d724c3f71..6413b3d51a 100644 --- a/umac/mlo_mgr/inc/utils_mlo.h +++ b/umac/mlo_mgr/inc/utils_mlo.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 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 @@ -106,6 +106,47 @@ util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t frame_len, bool isreassoc, qdf_size_t link_frame_maxsize, qdf_size_t *link_frame_len); +/** + * util_gen_link_probe_rsp_from_assoc_rsp() - Generate link specific + * probe response from assoc response. + * @frame: Pointer to original association response. This should not contain the + * 802.11 header, and must start from the fixed fields in the association + * response. This is required due to some caller semantics built into the end to + * end design. + * @frame_len: Length of original association response + * @link_id: Link ID for secondary links + * @link_addr: Secondary link's MAC address + * @link_frame: Generated secondary link specific association response. Note + * that this will start from the 802.11 header (unlike the original association + * response). This should be ignored in the case of failure. + * @link_frame_maxsize: Maximum size of generated secondary link specific + * association response + * @link_frame_len: Pointer to location where populated length of generated + * secondary link specific association response should be written. This should + * be ignored in the case of failure. + * @bcn_prb_ptr: Pointer to probe response of the current link on which assoc + * response is received, This should not contain the 802.11 header, and must + * start from the fixed fields in the probe response. + * @bcn_prb_len: Length of probe response of @bcn_prb_ptr. + * + * Generate a link specific logically equivalent probe response for the + * secondary link from the original association response containing a Multi-Link + * element. + * Currently, only two link MLO is supported. + * + * 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_gen_link_probe_rsp_from_assoc_rsp(uint8_t *frame, qdf_size_t frame_len, + uint8_t link_id, + struct qdf_mac_addr link_addr, + uint8_t *link_frame, + qdf_size_t link_frame_maxsize, + qdf_size_t *link_frame_len, + uint8_t *bcn_prb_ptr, + qdf_size_t bcn_prb_len); + /** * util_gen_link_probe_rsp() - Generate link specific probe response * @frame: Pointer to original probe response. This should not contain the @@ -598,6 +639,19 @@ util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t frame_len, bool isreassoc, return QDF_STATUS_E_NOSUPPORT; } +static inline QDF_STATUS +util_gen_link_probe_rsp_from_assoc_rsp(uint8_t *frame, qdf_size_t frame_len, + uint8_t link_id, + struct qdf_mac_addr link_addr, + uint8_t *link_frame, + qdf_size_t link_frame_maxsize, + qdf_size_t *link_frame_len, + uint8_t *bcn_prb_ptr, + qdf_size_t bcn_prb_len) +{ + return QDF_STATUS_E_NOSUPPORT; +} + static inline QDF_STATUS util_find_mlie(uint8_t *buf, qdf_size_t buflen, uint8_t **mlieseq, qdf_size_t *mlieseqlen) diff --git a/umac/mlo_mgr/src/utils_mlo.c b/umac/mlo_mgr/src/utils_mlo.c index 4c50760f1c..b1b25293be 100644 --- a/umac/mlo_mgr/src/utils_mlo.c +++ b/umac/mlo_mgr/src/utils_mlo.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2021, The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 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 @@ -1846,6 +1846,972 @@ util_find_bvmlie_persta_prof_for_linkid(uint8_t req_link_id, return QDF_STATUS_E_PROTO; } +static +QDF_STATUS util_gen_link_prb_rsp_from_assoc_rsp_cmn(uint8_t *frame, + qdf_size_t frame_len, + uint8_t orig_subtype, + uint8_t gen_subtype, + uint8_t req_link_id, + struct qdf_mac_addr link_addr, + uint8_t *link_frame, + qdf_size_t link_frame_maxsize, + qdf_size_t *link_frame_len, + uint8_t *bcn_prb_ptr, + qdf_size_t bcn_prb_len) +{ + /* Pointer to Multi-Link element/Multi-Link element fragment sequence */ + uint8_t *mlieseq; + /* Total length of Multi-Link element sequence (including fragments if + * any) + */ + qdf_size_t mlieseqlen; + /* Variant (i.e. type) of the Multi-Link element */ + enum wlan_ml_variant variant; + + /* Length of the payload of the Multi-Link element (inclusive of + * fragment payloads if any) without IE headers and element ID extension + */ + qdf_size_t mlieseqpayloadlen; + /* Pointer to copy of the payload of the Multi-Link element (inclusive + * of fragment payloads if any) without IE headers and element ID + * extension + */ + uint8_t *mlieseqpayload_copy; + + /* Pointer to start of Link Info within the copy of the payload of the + * Multi-Link element + */ + uint8_t *link_info; + /* Length of the Link Info */ + qdf_size_t link_info_len; + + /* Pointer to the IE section that occurs after the fixed fields in the + * original frame for the reporting STA. + */ + uint8_t *frame_iesection; + /* Offset to the start of the IE section in the original frame for the + * reporting STA. + */ + qdf_size_t frame_iesection_offset; + /* Total length of the IE section in the original frame for the + * reporting STA. + */ + qdf_size_t frame_iesection_len; + + /* Pointer to the IEEE802.11 frame header in the link specific frame + * being generated for the reported STA. + */ + struct wlan_frame_hdr *link_frame_hdr; + /* Current position in the link specific frame being generated for the + * reported STA. + */ + uint8_t *link_frame_currpos; + /* Current length of the link specific frame being generated for the + * reported STA. + */ + qdf_size_t link_frame_currlen; + + /* Pointer to IE for reporting STA */ + const uint8_t *reportingsta_ie; + /* Total size of IE for reporting STA, inclusive of the element header + */ + qdf_size_t reportingsta_ie_size; + + /* Pointer to current position in STA profile */ + uint8_t *sta_prof_currpos; + /* Remaining length of STA profile */ + qdf_size_t sta_prof_remlen; + /* Pointer to start of IE section in STA profile that occurs after fixed + * fields. + */ + uint8_t *sta_prof_iesection; + /* Total length of IE section in STA profile */ + qdf_size_t sta_prof_iesection_len; + /* Pointer to current position being processed in IE section in STA + * profile. + */ + uint8_t *sta_prof_iesection_currpos; + /* Remaining length of IE section in STA profile */ + qdf_size_t sta_prof_iesection_remlen; + + /* Pointer to IE in STA profile, that occurs within IE section */ + uint8_t *sta_prof_ie; + /* Total size of IE in STA profile, inclusive of the element header */ + qdf_size_t sta_prof_ie_size; + + /* Pointer to element ID list in Non-Inheritance IE */ + uint8_t *ninherit_elemlist; + /* Length of element ID list in Non-Inheritance IE */ + qdf_size_t ninherit_elemlist_len; + /* Pointer to element ID extension list in Non-Inheritance IE */ + uint8_t *ninherit_elemextlist; + /* Length of element ID extension list in Non-Inheritance IE */ + qdf_size_t ninherit_elemextlist_len; + /* Whether a given IE is in a non-inheritance list */ + bool is_in_noninheritlist; + + /* Whether MAC address of reported STA is valid */ + bool is_reportedmacaddr_valid; + /* MAC address of reported STA */ + struct qdf_mac_addr reportedmacaddr; + + /* Pointer to per-STA profile */ + uint8_t *persta_prof; + /* Length of the containing buffer which starts with the per-STA profile + */ + qdf_size_t persta_prof_bufflen; + + /* Other variables for temporary purposes */ + + /* Variable into which API for determining fragment information will + * indicate whether the element is the start of a fragment sequence or + * not. + */ + bool is_elemfragseq; + /* De-fragmented payload length returned by API for element + * defragmentation. + */ + qdf_size_t defragpayload_len; + /* Pointer to Beacon interval in STA info field */ + uint16_t beaconinterval; + /* Whether Beacon interval value valid */ + bool is_beaconinterval_valid; + /* TSF offset of the reproted AP */ + uint64_t tsfoffset; + /* TSF offset value valid */ + bool is_tsfoffset_valid; + /* If Complete Profile or not*/ + bool is_completeprofile; + qdf_size_t tmplen; + QDF_STATUS ret; + uint8_t ie_idx, linkid = 0xFF; + /* List of IEs to add from @bcn_prb_ptr */ + uint8_t missing_ie_list[] = {WLAN_ELEMID_SSID, WLAN_ELEMID_RSN}; + /* No.of IEs in @missing_ie_list array */ + uint8_t missing_ie_len = sizeof(missing_ie_list); + /* List of Extn IEs to add from @bcn_prb_ptr */ + uint8_t *missing_ext_ie_list = NULL; + /* No.of IEs in @missing_ext_ie_list array */ + uint8_t missing_ext_ie_len = 0x0; + uint8_t *bcn_prb_ies_ptr, *secondary_ie; + qdf_size_t bcn_prb_ies_len; + + if (!frame) { + mlo_err("Pointer to original frame is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (!frame_len) { + mlo_err("Length of original frame is zero"); + return QDF_STATUS_E_INVAL; + } + + if (!link_frame) { + mlo_err("Pointer to secondary link specific frame is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (!link_frame_maxsize) { + mlo_err("Maximum size of secondary link specific frame is zero"); + return QDF_STATUS_E_INVAL; + } + + if (!link_frame_len) { + mlo_err("Pointer to populated length of secondary link specific frame is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + + frame_iesection_offset = 0; + + /* This is a (re)association response */ + frame_iesection_offset = WLAN_ASSOC_RSP_IES_OFFSET; + + if (frame_len < frame_iesection_offset) { + /* The caller is supposed to have confirmed that this is a valid + * frame containing a Multi-Link element. Hence we treat this as + * a case of invalid argument being passed to us. + */ + mlo_err("Frame length %zu is smaller than the IE section offset %zu for orig_subtype %u", + frame_len, frame_iesection_offset, orig_subtype); + return QDF_STATUS_E_INVAL; + } + + frame_iesection_len = frame_len - frame_iesection_offset; + + if (frame_iesection_len == 0) { + /* The caller is supposed to have confirmed that this is a valid + * frame containing a Multi-Link element. Hence we treat this as + * a case of invalid argument being passed to us. + */ + mlo_err("No space left in frame for IE section"); + return QDF_STATUS_E_INVAL; + } + + frame_iesection = frame + frame_iesection_offset; + + bcn_prb_ies_ptr = bcn_prb_ptr + WLAN_PROBE_RESP_IES_OFFSET; + bcn_prb_ies_len = bcn_prb_len - WLAN_PROBE_RESP_IES_OFFSET; + + mlieseq = NULL; + mlieseqlen = 0; + + ret = util_find_mlie(frame_iesection, frame_iesection_len, &mlieseq, + &mlieseqlen); + if (QDF_IS_STATUS_ERROR(ret)) + return ret; + + if (!mlieseq) { + /* The caller is supposed to have confirmed that a Multi-Link + * element is present in the frame. Hence we treat this as a + * case of invalid argument being passed to us. + */ + mlo_err("Invalid original frame since no Multi-Link element found"); + return QDF_STATUS_E_INVAL; + } + + /* Sanity check the Multi-Link element sequence length */ + if (!mlieseqlen) { + mlo_err("Length of Multi-Link element sequence is zero. Investigate."); + return QDF_STATUS_E_FAILURE; + } + + 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_PROTO; + } + + ret = util_get_mlie_variant(mlieseq, mlieseqlen, (int *)&variant); + if (QDF_IS_STATUS_ERROR(ret)) + return ret; + + if (variant != WLAN_ML_VARIANT_BASIC) { + mlo_err_rl("Unexpected variant %u of Multi-Link element.", + variant); + return QDF_STATUS_E_PROTO; + } + + 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 per Multi-Link element search: %zu octets", + tmplen, mlieseqlen); + return QDF_STATUS_E_FAILURE; + } + + if (!mlieseqpayloadlen) { + mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate"); + return QDF_STATUS_E_FAILURE; + } + + mlo_debug("ML IE 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)) + goto mem_free; + + 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); + ret = QDF_STATUS_E_FAILURE; + goto mem_free; + } + } else { + qdf_mem_copy(mlieseqpayload_copy, + mlieseq + sizeof(struct ie_header) + 1, + mlieseqpayloadlen); + } + + link_info = NULL; + link_info_len = 0; + + ret = util_parse_multi_link_ctrl(mlieseqpayload_copy, + mlieseqpayloadlen, + &link_info, + &link_info_len); + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; + + /* As per the standard, the sender must include Link Info for + * association request/response. Throw an error if we are unable to + * obtain this. + */ + if (!link_info) { + mlo_err_rl("Unable to successfully obtain Link Info"); + ret = QDF_STATUS_E_PROTO; + goto mem_free; + } + + /* Note: We may have a future change to skip subelements which are not + * Per-STA Profile, handle more than two links in MLO, handle cases + * where we unexpectedly find more Per-STA Profiles than expected, etc. + */ + + persta_prof = NULL; + persta_prof_bufflen = 0; + + ret = util_find_bvmlie_persta_prof_for_linkid(req_link_id, + link_info, + link_info_len, + &persta_prof, + &persta_prof_bufflen); + + if (QDF_IS_STATUS_ERROR(ret)) { + mlo_err_rl("Per STA profile not found for link id %d", + req_link_id); + goto mem_free; + } + + sta_prof_remlen = 0; + sta_prof_currpos = NULL; + is_reportedmacaddr_valid = false; + is_beaconinterval_valid = false; + is_completeprofile = false; + is_tsfoffset_valid = false; + + /* Parse per-STA profile */ + ret = util_parse_bvmlie_perstaprofile_stactrl(persta_prof + + sizeof(struct subelem_header), + persta_prof_bufflen, + &linkid, + &beaconinterval, + &is_beaconinterval_valid, + &tsfoffset, + &is_tsfoffset_valid, + &is_completeprofile, + &is_reportedmacaddr_valid, + &reportedmacaddr, + true, + &sta_prof_currpos, + &sta_prof_remlen, + NULL, + NULL); + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; + + if (!is_completeprofile) { + mlo_err("Complete profile information is not present in per-STA profile"); + ret = QDF_STATUS_E_NOSUPPORT; + goto mem_free; + } + + /* We double check for a NULL STA Profile, though the helper function + * above would have taken care of this. We need to get a non-NULL STA + * profile, because we need to get at least the expected fixed fields, + * even if there is an (improbable) total inheritance. + */ + if (!sta_prof_currpos) { + mlo_err_rl("STA profile is NULL"); + ret = QDF_STATUS_E_PROTO; + goto mem_free; + } + + /* As per the standard, the sender sets the MAC address in the per-STA + * profile in association request/response. Without this, we cannot + * generate the link specific frame. + */ + if (!is_reportedmacaddr_valid) { + mlo_err_rl("Unable to get MAC address from per-STA profile"); + ret = QDF_STATUS_E_PROTO; + goto mem_free; + } + + link_frame_currpos = link_frame; + *link_frame_len = 0; + link_frame_currlen = 0; + + if (link_frame_maxsize < WLAN_MAC_HDR_LEN_3A) { + mlo_err("Insufficient space in link specific frame for 802.11 header. Required: %u octets, available: %zu octets", + WLAN_MAC_HDR_LEN_3A, link_frame_maxsize); + + ret = QDF_STATUS_E_NOMEM; + goto mem_free; + } + + link_frame_currpos += WLAN_MAC_HDR_LEN_3A; + link_frame_currlen += WLAN_MAC_HDR_LEN_3A; + + /* This is a (re)association response */ + mlo_debug("Populating fixed fields for (re)assoc resp in link specific frame"); + if (sta_prof_remlen < (WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN)) { + mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info + length of Status Code %u", + sta_prof_remlen, + WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN); + + ret = QDF_STATUS_E_PROTO; + goto mem_free; + } + + /* TODO: */ + qdf_mem_copy(link_frame_currpos, bcn_prb_ptr, WLAN_TIMESTAMP_LEN); + link_frame_currpos += WLAN_TIMESTAMP_LEN; + link_frame_currlen += WLAN_TIMESTAMP_LEN; + + if (is_beaconinterval_valid) { + qdf_mem_copy(link_frame_currpos, &beaconinterval, + WLAN_BEACONINTERVAL_LEN); + } else { + /* Use the BI from input frame if per-STA doesn't have valid + * BI + */ + qdf_mem_copy(link_frame_currpos, + bcn_prb_ptr + WLAN_TIMESTAMP_LEN, + WLAN_BEACONINTERVAL_LEN); + } + link_frame_currpos += WLAN_BEACONINTERVAL_LEN; + link_frame_currlen += WLAN_BEACONINTERVAL_LEN; + + /* Capability information and Status Code are specific to the + * link. Copy these from the STA profile. + */ + + if ((link_frame_maxsize - link_frame_currlen) < + WLAN_CAPABILITYINFO_LEN) { + mlo_err("Insufficient space in link specific frame for Capability Info and Status Code fields. Required: %u octets, available: %zu octets", + WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN, + (link_frame_maxsize - link_frame_currlen)); + + ret = QDF_STATUS_E_NOMEM; + goto mem_free; + } + + qdf_mem_copy(link_frame_currpos, sta_prof_currpos, + WLAN_CAPABILITYINFO_LEN); + link_frame_currpos += WLAN_CAPABILITYINFO_LEN; + link_frame_currlen += WLAN_CAPABILITYINFO_LEN; + mlo_debug("Added Capability Info (%u octets) to link specific frame", + WLAN_CAPABILITYINFO_LEN); + + sta_prof_currpos += WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN; + sta_prof_remlen -= WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN; + + sta_prof_iesection = sta_prof_currpos; + sta_prof_iesection_len = sta_prof_remlen; + + /* Populate non-inheritance lists if applicable */ + ninherit_elemlist_len = 0; + ninherit_elemlist = NULL; + ninherit_elemextlist_len = 0; + ninherit_elemextlist = NULL; + + ret = util_get_noninheritlists(sta_prof_iesection, + sta_prof_iesection_len, + &ninherit_elemlist, + &ninherit_elemlist_len, + &ninherit_elemextlist, + &ninherit_elemextlist_len); + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; + + /* Go through IEs of the reporting STA, and those in STA profile, merge + * them into link_frame (except for elements in the Non-Inheritance + * list). + * + * Note: Currently, only 2-link MLO is supported here. We may have a + * future change to expand to more links. + */ + reportingsta_ie = util_find_eid(WLAN_ELEMID_SSID, frame_iesection, + frame_iesection_len); + + /* This is a (re)association response. Sanity check that the + * SSID element is present neither for the reporting STA nor in + * the STA profile. + */ + if (reportingsta_ie) { + mlo_err_rl("SSID element found for reporting STA for (re)association response. It should not be present."); + ret = QDF_STATUS_E_PROTO; + goto mem_free; + } + + sta_prof_ie = util_find_eid(WLAN_ELEMID_SSID, + sta_prof_iesection, + sta_prof_iesection_len); + + if (sta_prof_ie) { + mlo_err_rl("SSID element found in STA profile for (re)association response. It should not be present."); + ret = QDF_STATUS_E_PROTO; + goto mem_free; + } + + reportingsta_ie = reportingsta_ie ? reportingsta_ie : frame_iesection; + + ret = util_validate_reportingsta_ie(reportingsta_ie, frame_iesection, + frame_iesection_len); + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; + + reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN; + + while (((reportingsta_ie + reportingsta_ie_size) - frame_iesection) + <= frame_iesection_len) { + if (reportingsta_ie[ID_POS] != WLAN_ELEMID_EXTN_ELEM) { + for (ie_idx = 0; ie_idx < missing_ie_len; ie_idx++) { + if (missing_ie_list[ie_idx] == + reportingsta_ie[ID_POS]) { + missing_ie_list[ie_idx] = + WLAN_ELEMID_EXTN_ELEM; + } + } + } else { + for (ie_idx = 0; ie_idx < missing_ext_ie_len; ie_idx++) { + if (missing_ext_ie_list[ie_idx] == + reportingsta_ie[IDEXT_POS]) { + missing_ext_ie_list[ie_idx] = + WLAN_ELEMID_EXTN_ELEM; + } + } + } + + /* Skip Multi-Link element */ + if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) && + (reportingsta_ie[IDEXT_POS] == + WLAN_EXTN_ELEMID_MULTI_LINK)) { + if (((reportingsta_ie + reportingsta_ie_size) - + frame_iesection) == frame_iesection_len) + break; + + /* Add BV ML IE for link specific probe response */ + ret = util_add_mlie_for_prb_rsp_gen( + reportingsta_ie, + reportingsta_ie[TAG_LEN_POS], + &link_frame_currpos, + &link_frame_currlen, + link_frame_maxsize, + linkid); + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; + + reportingsta_ie += reportingsta_ie_size; + + ret = util_validate_reportingsta_ie(reportingsta_ie, + frame_iesection, + frame_iesection_len); + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; + + reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + + MIN_IE_LEN; + + continue; + } + + sta_prof_ie = NULL; + sta_prof_ie_size = 0; + + if (sta_prof_iesection_len) { + if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) { + sta_prof_ie = (uint8_t *)util_find_extn_eid(reportingsta_ie[ID_POS], + reportingsta_ie[IDEXT_POS], + sta_prof_iesection, + sta_prof_iesection_len); + } else { + sta_prof_ie = (uint8_t *)util_find_eid(reportingsta_ie[ID_POS], + sta_prof_iesection, + sta_prof_iesection_len); + } + } + + if (!sta_prof_ie) { + /* IE is present for reporting STA, but not in STA + * profile. + */ + + is_in_noninheritlist = false; + + ret = util_eval_ie_in_noninheritlist((uint8_t *)reportingsta_ie, + reportingsta_ie_size, + ninherit_elemlist, + ninherit_elemlist_len, + ninherit_elemextlist, + ninherit_elemextlist_len, + &is_in_noninheritlist); + + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; + + if (!is_in_noninheritlist) { + if ((link_frame_currpos + + reportingsta_ie_size) <= + (link_frame + link_frame_maxsize)) { + qdf_mem_copy(link_frame_currpos, + reportingsta_ie, + reportingsta_ie_size); + + link_frame_currpos += + reportingsta_ie_size; + link_frame_currlen += + reportingsta_ie_size; + + if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) { + mlo_etrace_debug("IE with element ID : %u extension element ID : %u (%zu octets) present for reporting STA but not in STA profile. Copied IE from reporting frame to link specific frame", + reportingsta_ie[ID_POS], + reportingsta_ie[IDEXT_POS], + reportingsta_ie_size); + } else { + mlo_etrace_debug("IE with element ID : %u (%zu octets) present for reporting STA but not in STA profile. Copied IE from reporting frame to link specific frame", + reportingsta_ie[ID_POS], + reportingsta_ie_size); + } + } else { + if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) { + mlo_etrace_err_rl("Insufficient space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets", + reportingsta_ie[ID_POS], + reportingsta_ie[IDEXT_POS], + reportingsta_ie_size, + link_frame_maxsize - + link_frame_currlen); + } else { + mlo_etrace_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets", + reportingsta_ie[ID_POS], + reportingsta_ie_size, + link_frame_maxsize - + link_frame_currlen); + } + + ret = QDF_STATUS_E_NOMEM; + goto mem_free; + } + } else { + if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) { + mlo_etrace_debug("IE with element ID : %u extension element ID : %u (%zu octets) present for reporting STA but not in STA profile. However it is in Non-Inheritance list, hence ignoring.", + reportingsta_ie[ID_POS], + reportingsta_ie[IDEXT_POS], + reportingsta_ie_size); + } else { + mlo_etrace_debug("IE with element ID : %u (%zu octets) present for reporting STA but not in STA profile. However it is in Non-Inheritance list, hence ignoring.", + reportingsta_ie[ID_POS], + reportingsta_ie_size); + } + } + } else { + /* IE is present for reporting STA and also in STA + * profile, copy from STA profile and flag the IE in STA + * profile as copied (by setting EID field to 0). The + * SSID element (with EID 0) is processed first to + * enable this. For vendor IE, compare OUI + type + + * subType to determine if they are the same IE. + */ + /* Note: This may be revisited in a future change, to + * adhere to provisions in the standard for multiple + * occurrences of a given element ID/extension element + * ID. + */ + + ret = util_validate_sta_prof_ie(sta_prof_ie, + sta_prof_iesection, + sta_prof_iesection_len); + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; + + sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + + MIN_IE_LEN; + + sta_prof_iesection_remlen = + sta_prof_iesection_len - + (sta_prof_ie - sta_prof_iesection); + + if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_VENDOR) && + (sta_prof_iesection_remlen >= MIN_VENDOR_TAG_LEN)) { + /* If Vendor IE also presents in STA profile, + * then ignore the Vendor IE which is for + * reporting STA. It only needs to copy Vendor + * IE from STA profile to link specific frame. + * The copy happens when going through the + * remaining IEs. + */ + ; + } else { + /* Copy IE from STA profile into link specific + * frame. + */ + if ((link_frame_currpos + sta_prof_ie_size) <= + (link_frame + link_frame_maxsize)) { + qdf_mem_copy(link_frame_currpos, + sta_prof_ie, + sta_prof_ie_size); + + link_frame_currpos += sta_prof_ie_size; + link_frame_currlen += + sta_prof_ie_size; + + if (reportingsta_ie[ID_POS] == + WLAN_ELEMID_EXTN_ELEM) { + mlo_etrace_debug("IE with element ID : %u extension element ID : %u (%zu octets) for reporting STA also present in STA profile. Copied IE from STA profile to link specific frame", + sta_prof_ie[ID_POS], + sta_prof_ie[IDEXT_POS], + sta_prof_ie_size); + } else { + mlo_etrace_debug("IE with element ID : %u (%zu octets) for reporting STA also present in STA profile. Copied IE from STA profile to link specific frame", + sta_prof_ie[ID_POS], + sta_prof_ie_size); + } + + sta_prof_ie[0] = 0; + } else { + if (sta_prof_ie[ID_POS] == + WLAN_ELEMID_EXTN_ELEM) { + mlo_etrace_err_rl("Insufficient space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets", + sta_prof_ie[ID_POS], + sta_prof_ie[IDEXT_POS], + sta_prof_ie_size, + link_frame_maxsize - + link_frame_currlen); + } else { + mlo_etrace_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets", + sta_prof_ie[ID_POS], + sta_prof_ie_size, + link_frame_maxsize - + link_frame_currlen); + } + + ret = QDF_STATUS_E_NOMEM; + goto mem_free; + } + } + } + + if (((reportingsta_ie + reportingsta_ie_size) - + frame_iesection) == frame_iesection_len) + break; + + reportingsta_ie += reportingsta_ie_size; + + ret = util_validate_reportingsta_ie(reportingsta_ie, + frame_iesection, + frame_iesection_len); + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; + + reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + + MIN_IE_LEN; + } + + /* Go through the remaining unprocessed IEs in STA profile and copy them + * to the link specific frame. The processed ones are marked with 0 in + * the first octet. The first octet corresponds to the element ID. In + * the case of (re)association request, the element with actual ID + * WLAN_ELEMID_SSID(0) has already been copied to the link specific + * frame. In the case of (re)association response, it has been verified + * that the element with actual ID WLAN_ELEMID_SSID(0) is present + * neither for the reporting STA nor in the STA profile. + */ + sta_prof_iesection_currpos = sta_prof_iesection; + sta_prof_iesection_remlen = sta_prof_iesection_len; + + while (sta_prof_iesection_remlen > 0) { + sta_prof_ie = sta_prof_iesection_currpos; + ret = util_validate_sta_prof_ie(sta_prof_ie, + sta_prof_iesection_currpos, + sta_prof_iesection_remlen); + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; + + sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + MIN_IE_LEN; + + if (!sta_prof_ie[0] || (sta_prof_ie[0] == 0xff && sta_prof_ie[1] > 0 && sta_prof_ie[2] == 0x38)) { + /* Skip this, since it has already been processed + * or it is non inheritance IE which is not required + */ + sta_prof_iesection_currpos += sta_prof_ie_size; + sta_prof_iesection_remlen -= sta_prof_ie_size; + continue; + } + + /* Copy IE from STA profile into link specific frame. */ + if ((link_frame_currpos + sta_prof_ie_size) <= + (link_frame + link_frame_maxsize)) { + if (sta_prof_ie[ID_POS] != WLAN_ELEMID_EXTN_ELEM) { + for (ie_idx = 0; ie_idx < missing_ie_len; ie_idx++) { + if (missing_ie_list[ie_idx] == + sta_prof_ie[ID_POS]) { + missing_ie_list[ie_idx] = + WLAN_ELEMID_EXTN_ELEM; + } + } + } else { + for (ie_idx = 0; ie_idx < missing_ext_ie_len; ie_idx++) { + if (missing_ext_ie_list[ie_idx] == + sta_prof_ie[IDEXT_POS]) { + missing_ext_ie_list[ie_idx] = + WLAN_ELEMID_EXTN_ELEM; + } + } + } + + qdf_mem_copy(link_frame_currpos, + sta_prof_ie, + sta_prof_ie_size); + + link_frame_currpos += sta_prof_ie_size; + link_frame_currlen += + sta_prof_ie_size; + + if (reportingsta_ie[ID_POS] == + WLAN_ELEMID_EXTN_ELEM) { + mlo_etrace_debug("IE with element ID : %u extension element ID : %u (%zu octets) is present only in STA profile. Copied IE from STA profile to link specific frame", + sta_prof_ie[ID_POS], + sta_prof_ie[IDEXT_POS], + sta_prof_ie_size); + } else { + mlo_etrace_debug("IE with element ID : %u (%zu octets) is present only in STA profile. Copied IE from STA profile to link specific frame", + sta_prof_ie[ID_POS], + sta_prof_ie_size); + } + + sta_prof_ie[0] = 0; + } else { + if (sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) { + mlo_etrace_err_rl("Insufficient space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets", + sta_prof_ie[ID_POS], + sta_prof_ie[IDEXT_POS], + sta_prof_ie_size, + link_frame_maxsize - + link_frame_currlen); + } else { + mlo_etrace_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets", + sta_prof_ie[ID_POS], + sta_prof_ie_size, + link_frame_maxsize - + link_frame_currlen); + } + + ret = QDF_STATUS_E_NOMEM; + goto mem_free; + } + + sta_prof_iesection_currpos += sta_prof_ie_size; + sta_prof_iesection_remlen -= sta_prof_ie_size; + } + + /* Copy missing IEs from probe resp of original link */ + for (ie_idx = 0; ie_idx < missing_ie_len; ie_idx++) { + if (missing_ie_list[ie_idx] == WLAN_ELEMID_EXTN_ELEM) + continue; + + secondary_ie = util_find_eid(missing_ie_list[ie_idx], + bcn_prb_ies_ptr, bcn_prb_ies_len); + if (!secondary_ie) { + mlo_etrace_err_rl("IE %u not found in secondary probe resp buffer", + missing_ie_list[ie_idx]); + continue; + } + + if ((link_frame_currpos + secondary_ie[TAG_LEN_POS]) > + (link_frame + link_frame_maxsize)) { + mlo_etrace_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets", + secondary_ie[ID_POS], + secondary_ie[TAG_LEN_POS], + link_frame_maxsize - + link_frame_currlen); + continue; + } + + qdf_mem_copy(link_frame_currpos, secondary_ie, + secondary_ie[TAG_LEN_POS] + MIN_IE_LEN); + link_frame_currpos += secondary_ie[TAG_LEN_POS] + MIN_IE_LEN; + link_frame_currlen += secondary_ie[TAG_LEN_POS] + MIN_IE_LEN; + } + + /* Copy missing extn IEs from probe resp of original link */ + for (ie_idx = 0; ie_idx < missing_ext_ie_len; ie_idx++) { + if (missing_ext_ie_list[ie_idx] == WLAN_ELEMID_EXTN_ELEM) + continue; + + secondary_ie = util_find_extn_eid(WLAN_ELEMID_EXTN_ELEM, + missing_ext_ie_list[ie_idx], + bcn_prb_ies_ptr, + bcn_prb_ies_len); + if (!secondary_ie) { + mlo_etrace_err_rl("Extn IE %u not found in secondary probe resp buffer", + missing_iext_ie_list[ie_idx]); + continue; + } + + if ((link_frame_currpos + secondary_ie[TAG_LEN_POS]) > + (link_frame + link_frame_maxsize)) { + mlo_etrace_err_rl("Insufficient space in link specific frame for IE with extn element ID : %u. Required: %zu octets, available: %zu octets", + secondary_ie[IDEXT_POS], + secondary_ie[TAG_LEN_POS], + link_frame_maxsize - + link_frame_currlen); + continue; + } + + qdf_mem_copy(link_frame_currpos, secondary_ie, + secondary_ie[TAG_LEN_POS] + MIN_IE_LEN); + link_frame_currpos += secondary_ie[TAG_LEN_POS] + MIN_IE_LEN; + link_frame_currlen += secondary_ie[TAG_LEN_POS] + MIN_IE_LEN; + } + + /* Copy the link MAC addr */ + link_frame_hdr = (struct wlan_frame_hdr *)link_frame; + + qdf_mem_copy(link_frame_hdr->i_addr3, reportedmacaddr.bytes, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes, + QDF_MAC_ADDR_SIZE); + qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr, QDF_MAC_ADDR_SIZE); + + link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_PROBE_RESP_FC0; + link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_PROBE_RESP_FC1; + + mlo_debug("subtype:%u addr3:" QDF_MAC_ADDR_FMT " addr2:" + QDF_MAC_ADDR_FMT " addr1:" QDF_MAC_ADDR_FMT, + gen_subtype, + QDF_MAC_ADDR_REF(link_frame_hdr->i_addr3), + QDF_MAC_ADDR_REF(link_frame_hdr->i_addr2), + QDF_MAC_ADDR_REF(link_frame_hdr->i_addr1)); + + /* Seq num not used so not populated */ + + *link_frame_len = link_frame_currlen; + + qdf_err("Generated ML Probe from assoc resp"); + qdf_trace_hex_dump(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG, + link_frame, link_frame_currlen); + + ret = QDF_STATUS_SUCCESS; + +mem_free: + qdf_mem_free(mlieseqpayload_copy); + return ret; +} + static QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, uint8_t subtype, @@ -2142,7 +3108,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, if (!mlieseqpayload_copy) { mlo_err_rl("Could not allocate memory for Multi-Link element payload copy"); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; } if (is_elemfragseq) { @@ -2152,16 +3119,14 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, mlieseqpayload_copy, mlieseqpayloadlen, &defragpayload_len); - if (QDF_IS_STATUS_ERROR(ret)) { - qdf_mem_free(mlieseqpayload_copy); - return ret; - } + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; 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; + ret = QDF_STATUS_E_FAILURE; + goto mem_free; } } else { qdf_mem_copy(mlieseqpayload_copy, @@ -2176,10 +3141,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, mlieseqpayloadlen, &link_info, &link_info_len); - if (QDF_IS_STATUS_ERROR(ret)) { - qdf_mem_free(mlieseqpayload_copy); - return ret; - } + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; /* As per the standard, the sender must include Link Info for * association request/response. Throw an error if we are unable to @@ -2187,8 +3150,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, */ if (!link_info) { mlo_err_rl("Unable to successfully obtain Link Info"); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_PROTO; + ret = QDF_STATUS_E_PROTO; + goto mem_free; } /* Note: We may have a future change to skip subelements which are not @@ -2208,8 +3171,7 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, if (QDF_IS_STATUS_ERROR(ret)) { mlo_err_rl("Per STA profile not found for link id %d", req_link_id); - qdf_mem_free(mlieseqpayload_copy); - return ret; + goto mem_free; } sta_prof_remlen = 0; @@ -2236,14 +3198,13 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, &sta_prof_remlen, NULL, NULL); - if (QDF_IS_STATUS_ERROR(ret)) { - qdf_mem_free(mlieseqpayload_copy); - return ret; - } + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; if (subtype == WLAN_FC0_STYPE_PROBE_RESP && !is_completeprofile) { mlo_err("Complete profile information is not present in per-STA profile of probe response frame"); - return QDF_STATUS_E_NOSUPPORT; + ret = QDF_STATUS_E_NOSUPPORT; + goto mem_free; } /* We double check for a NULL STA Profile, though the helper function @@ -2253,8 +3214,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, */ if (!sta_prof_currpos) { mlo_err_rl("STA profile is NULL"); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_PROTO; + ret = QDF_STATUS_E_PROTO; + goto mem_free; } /* As per the standard, the sender sets the MAC address in the per-STA @@ -2263,8 +3224,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, */ if (!is_reportedmacaddr_valid) { mlo_err_rl("Unable to get MAC address from per-STA profile"); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_PROTO; + ret = QDF_STATUS_E_PROTO; + goto mem_free; } link_frame_currpos = link_frame; @@ -2275,8 +3236,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, mlo_err("Insufficient space in link specific frame for 802.11 header. Required: %u octets, available: %zu octets", WLAN_MAC_HDR_LEN_3A, link_frame_maxsize); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; } link_frame_currpos += WLAN_MAC_HDR_LEN_3A; @@ -2291,8 +3252,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, sta_prof_remlen, WLAN_CAPABILITYINFO_LEN); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_PROTO; + ret = QDF_STATUS_E_PROTO; + goto mem_free; } /* Capability information is specific to the link. Copy this @@ -2305,8 +3266,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, WLAN_CAPABILITYINFO_LEN, (link_frame_maxsize - link_frame_currlen)); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; } qdf_mem_copy(link_frame_currpos, sta_prof_currpos, @@ -2327,8 +3288,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, WLAN_LISTENINTERVAL_LEN, (link_frame_maxsize - link_frame_currlen)); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; } qdf_mem_copy(link_frame_currpos, @@ -2348,8 +3309,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, (link_frame_maxsize - link_frame_currlen)); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; } qdf_mem_copy(link_frame_currpos, @@ -2373,8 +3334,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_PROTO; + ret = QDF_STATUS_E_PROTO; + goto mem_free; } /* Capability information and Status Code are specific to the @@ -2387,8 +3348,9 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN, (link_frame_maxsize - link_frame_currlen)); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; + } qdf_mem_copy(link_frame_currpos, sta_prof_currpos, @@ -2414,8 +3376,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, WLAN_AID_LEN, (link_frame_maxsize - link_frame_currlen)); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; } qdf_mem_copy(link_frame_currpos, @@ -2436,8 +3398,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, WLAN_TIMESTAMP_LEN, (link_frame_maxsize - link_frame_currlen)); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; } /* Per spec 11be_D2.1.1, the TSF Offset subfield of the STA Info @@ -2456,8 +3418,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, if (!is_beaconinterval_valid) { mlo_err_rl("Beacon interval information not present in STA info field of per-STA profile"); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_PROTO; + ret = QDF_STATUS_E_PROTO; + goto mem_free; } /* Beacon Interval information copy this from @@ -2469,8 +3431,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, WLAN_BEACONINTERVAL_LEN, (link_frame_maxsize - link_frame_currlen)); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; } qdf_mem_copy(link_frame_currpos, &beaconinterval, @@ -2483,8 +3445,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, sta_prof_remlen, WLAN_CAPABILITYINFO_LEN); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_PROTO; + ret = QDF_STATUS_E_PROTO; + goto mem_free; } /* Capability information is specific to the link. Copy this @@ -2497,8 +3459,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, WLAN_CAPABILITYINFO_LEN, (link_frame_maxsize - link_frame_currlen)); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; } qdf_mem_copy(link_frame_currpos, sta_prof_currpos, @@ -2525,10 +3487,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, &ninherit_elemlist_len, &ninherit_elemextlist, &ninherit_elemextlist_len); - if (QDF_IS_STATUS_ERROR(ret)) { - qdf_mem_free(mlieseqpayload_copy); - return ret; - } + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; /* Go through IEs of the reporting STA, and those in STA profile, merge * them into link_frame (except for elements in the Non-Inheritance @@ -2550,8 +3510,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, */ if (!reportingsta_ie) { mlo_err_rl("SSID element not found in reporting STA of the frame."); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_PROTO; + ret = QDF_STATUS_E_PROTO; + goto mem_free; } } else { /* This is a (re)association response. Sanity check that the @@ -2560,8 +3520,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, */ if (reportingsta_ie) { mlo_err_rl("SSID element found for reporting STA for (re)association response. It should not be present."); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_PROTO; + ret = QDF_STATUS_E_PROTO; + goto mem_free; } sta_prof_ie = util_find_eid(WLAN_ELEMID_SSID, @@ -2570,8 +3530,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, if (sta_prof_ie) { mlo_err_rl("SSID element found in STA profile for (re)association response. It should not be present."); - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_PROTO; + ret = QDF_STATUS_E_PROTO; + goto mem_free; } } @@ -2579,10 +3539,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, ret = util_validate_reportingsta_ie(reportingsta_ie, frame_iesection, frame_iesection_len); - if (QDF_IS_STATUS_ERROR(ret)) { - qdf_mem_free(mlieseqpayload_copy); - return ret; - } + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN; @@ -2605,20 +3563,16 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, &link_frame_currlen, link_frame_maxsize, linkid); - if (QDF_IS_STATUS_ERROR(ret)) { - qdf_mem_free(mlieseqpayload_copy); - return ret; - } + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; } reportingsta_ie += reportingsta_ie_size; ret = util_validate_reportingsta_ie(reportingsta_ie, frame_iesection, frame_iesection_len); - if (QDF_IS_STATUS_ERROR(ret)) { - qdf_mem_free(mlieseqpayload_copy); - return ret; - } + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN; @@ -2657,10 +3611,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, ninherit_elemextlist_len, &is_in_noninheritlist); - if (QDF_IS_STATUS_ERROR(ret)) { - qdf_mem_free(mlieseqpayload_copy); - return ret; - } + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; if (!is_in_noninheritlist) { if ((link_frame_currpos + @@ -2701,8 +3653,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, link_frame_currlen); } - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; } } else { if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) { @@ -2733,10 +3685,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, ret = util_validate_sta_prof_ie(sta_prof_ie, sta_prof_iesection, sta_prof_iesection_len); - if (QDF_IS_STATUS_ERROR(ret)) { - qdf_mem_free(mlieseqpayload_copy); - return ret; - } + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + MIN_IE_LEN; @@ -2799,8 +3749,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, link_frame_currlen); } - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; } } } @@ -2814,10 +3764,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, ret = util_validate_reportingsta_ie(reportingsta_ie, frame_iesection, frame_iesection_len); - if (QDF_IS_STATUS_ERROR(ret)) { - qdf_mem_free(mlieseqpayload_copy); - return ret; - } + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN; @@ -2840,10 +3788,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, ret = util_validate_sta_prof_ie(sta_prof_ie, sta_prof_iesection_currpos, sta_prof_iesection_remlen); - if (QDF_IS_STATUS_ERROR(ret)) { - qdf_mem_free(mlieseqpayload_copy); - return ret; - } + if (QDF_IS_STATUS_ERROR(ret)) + goto mem_free; sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + MIN_IE_LEN; @@ -2894,8 +3840,8 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, link_frame_currlen); } - qdf_mem_free(mlieseqpayload_copy); - return QDF_STATUS_E_NOMEM; + ret = QDF_STATUS_E_NOMEM; + goto mem_free; } sta_prof_iesection_currpos += sta_prof_ie_size; @@ -2949,11 +3895,12 @@ QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len, /* Seq num not used so not populated */ - qdf_mem_free(mlieseqpayload_copy); - *link_frame_len = link_frame_currlen; + ret = QDF_STATUS_SUCCESS; - return QDF_STATUS_SUCCESS; +mem_free: + qdf_mem_free(mlieseqpayload_copy); + return ret; } QDF_STATUS @@ -2971,6 +3918,25 @@ util_gen_link_assoc_req(uint8_t *frame, qdf_size_t frame_len, bool isreassoc, link_frame_maxsize, link_frame_len); } +QDF_STATUS +util_gen_link_probe_rsp_from_assoc_rsp(uint8_t *frame, qdf_size_t frame_len, + uint8_t link_id, struct qdf_mac_addr link_addr, + uint8_t *link_frame, qdf_size_t link_frame_maxsize, + qdf_size_t *link_frame_len, + uint8_t *bcn_prb_ptr, + qdf_size_t bcn_prb_len) +{ + return util_gen_link_prb_rsp_from_assoc_rsp_cmn(frame, frame_len, + WLAN_FC0_STYPE_ASSOC_RESP, + WLAN_FC0_STYPE_PROBE_RESP, + link_id, link_addr, + link_frame, + link_frame_maxsize, + link_frame_len, + bcn_prb_ptr, + bcn_prb_len); +} + QDF_STATUS util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t frame_len, bool isreassoc, uint8_t link_id, diff --git a/umac/scan/dispatcher/inc/wlan_scan_public_structs.h b/umac/scan/dispatcher/inc/wlan_scan_public_structs.h index 039591afc0..55c1c1f1e5 100644 --- a/umac/scan/dispatcher/inc/wlan_scan_public_structs.h +++ b/umac/scan/dispatcher/inc/wlan_scan_public_structs.h @@ -529,6 +529,8 @@ struct reduced_neighbor_report { * @ecsa_ie: Pointer to eCSA IE * @max_cst_ie: Pointer to Max Channel Switch Time IE * @is_valid_link: The partner link can be used if true + * @is_scan_entry_not_found: If set to true, the partner link scan entry is + * not present in scan DB (currently using for non-TxMBSSID MLO AP) * @op_class: Operating class */ struct partner_link_info { @@ -539,7 +541,8 @@ struct partner_link_info { const uint8_t *csa_ie; const uint8_t *ecsa_ie; const uint8_t *max_cst_ie; - uint8_t is_valid_link; + bool is_valid_link; + bool is_scan_entry_not_found; uint8_t op_class; };