From 01e1c46c4e27bd967187d35318504cf36726f8bd Mon Sep 17 00:00:00 2001 From: Vinod Kumar Pirla Date: Mon, 25 Dec 2023 23:52:28 -0800 Subject: [PATCH] qcacmn: Generate ML prb rsp from assoc rsp For non-TxMBSSID ML AP ML probe req may not receive any response from MBSSID AP and later partner link may fail to connect as scan entry is not present. Introduce new flag to suggest whether the partner link scan entry is present or not in the scan DB. If flag is set to true, generate scan entry for such links. Introduce new APIs which will generate ML probe resp using the per-STA profile of partner link from assoc resp frame. Introduce API to get the current candidate scan entry from connect request. Change-Id: I1c33956b01eb468afa26be5b0bfba634ee3a0aee CRs-Fixed: 3675830 --- .../core/src/wlan_cm_bss_scoring.c | 13 +- .../connection_mgr/core/src/wlan_cm_connect.c | 4 +- .../core/src/wlan_cm_main_api.h | 15 +- .../connection_mgr/core/src/wlan_cm_util.c | 43 +- .../dispatcher/inc/wlan_cm_api.h | 15 +- .../dispatcher/inc/wlan_cm_bss_score_param.h | 12 +- .../dispatcher/src/wlan_cm_api.c | 9 +- umac/mlo_mgr/inc/utils_mlo.h | 56 +- umac/mlo_mgr/src/utils_mlo.c | 1162 +++++++++++++++-- .../dispatcher/inc/wlan_scan_public_structs.h | 5 +- 10 files changed, 1216 insertions(+), 118 deletions(-) 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; };