diff --git a/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h index 6fd5e885eb..abc5d2eb16 100644 --- a/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h +++ b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h @@ -4919,4 +4919,23 @@ uint32_t policy_mgr_get_connection_count_with_ch_freq(uint32_t ch_freq); bool policy_mgr_is_sap_allowed_on_indoor(struct wlan_objmgr_pdev *pdev, uint8_t vdev_id, qdf_freq_t ch_freq); +#ifdef WLAN_FEATURE_TDLS_CONCURRENCIES +/** + * policy_mgr_get_allowed_tdls_offchannel_freq() - Check if TDLS off-channel is + * allowed during concurrency. When off-channel is allowed, update the provided + * input channel frequency with concurrent vdev frequency in DBS case. + * Fill the provided channel frequency as 0 if all 5GHz/6GHz channels are + * allowed for off-channel operation in SCC case. + * Don't allow off channel operation in any MCC case. + * @psoc: psoc pointer + * @vdev: vdev pointer + * @ch_freq: Frequency pointer + * + * Return: true or false based on current concurrency combination + */ +bool +policy_mgr_get_allowed_tdls_offchannel_freq(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + qdf_freq_t *ch_freq); +#endif /* WLAN_FEATURE_TDLS_CONCURRENCIES */ #endif /* __WLAN_POLICY_MGR_API_H */ diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_action.c b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_action.c index c422d4783a..d2949fc78b 100644 --- a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_action.c +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_action.c @@ -3543,3 +3543,122 @@ ret_value: return value; } +#ifdef WLAN_FEATURE_TDLS_CONCURRENCIES +bool +policy_mgr_get_allowed_tdls_offchannel_freq(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_vdev *vdev, + qdf_freq_t *ch_freq) +{ + struct connection_info info[MAX_NUMBER_OF_CONC_CONNECTIONS] = {0}; + uint8_t connection_count, i, j, sta_vdev_id; + + *ch_freq = 0; + /* + * TDLS off channel is not allowed in any MCC scenario + */ + if (policy_mgr_current_concurrency_is_mcc(psoc)) { + policy_mgr_dump_current_concurrency(psoc); + policy_mgr_debug("TDLS off channel not allowed in MCC"); + return false; + } + + /* + * TDLS offchannel is done only when STA is connected on 2G channel and + * the current concurrency is not MCC + */ + if (!policy_mgr_is_sta_connected_2g(psoc)) { + policy_mgr_debug("STA not-connected on 2.4 Ghz"); + return false; + } + + /* + * 2 Port DBS scenario - Allow non-STA vdev channel for + * TDLS off-channel operation + * + * 3 Port Scenario - If STA Vdev is on SCC, allow TDLS off-channel on + * the channel of vdev on the other MAC + * If STA vdev is standalone on one mac, and scc on another mac, then + * allow TDLS off channel on other mac scc channel + */ + sta_vdev_id = wlan_vdev_get_id(vdev); + connection_count = policy_mgr_get_connection_info(psoc, info); + switch (connection_count) { + case 1: + return true; + case 2: + /* + * Allow all the 5GHz/6GHz channels when STA is in SCC + */ + if (policy_mgr_current_concurrency_is_scc(psoc)) { + *ch_freq = 0; + return true; + } else if (policy_mgr_is_current_hwmode_dbs(psoc)) { + /* + * In DBS case, allow off-channel operation on the + * other mac 5GHz/6GHz channel where the STA is not + * present + * Don't consider SBS case since STA should be + * connected in 2.4GHz channel for TDLS + * off-channel and MCC on SBS ex. 3 PORT + * 2.4GHz STA + 5GHz Lower MCC + 5GHz Upper will + * not be allowed + */ + if (sta_vdev_id == info[0].vdev_id) + *ch_freq = info[1].ch_freq; + else + *ch_freq = info[0].ch_freq; + + return true; + } + + break; + case 3: + + /* + * 3 Vdev SCC on 2.4GHz band. Allow TDLS off-channel operation + * on all the 5GHz & 6GHz channels + */ + if (info[0].ch_freq == info[1].ch_freq && + info[0].ch_freq == info[2].ch_freq) { + *ch_freq = 0; + return true; + } + + /* + * DBS with SCC on one vdev scenario. Allow TDLS off-channel + * on other mac frequency where STA is not present + * SBS case is not considered since STA should be connected + * on 2.4GHz and TDLS off-channel on SBS MCC is not allowed + */ + for (i = 0; i < connection_count; i++) { + for (j = i + 1; j < connection_count; j++) { + /* + * Find 2 vdevs such that STA is one of the vdev + * and STA + other vdev are not on same mac. + * Return the foreign vdev frequency which is + * not on same mac along with STA + */ + if (!policy_mgr_2_freq_always_on_same_mac( + psoc, info[i].ch_freq, + info[j].ch_freq)) { + if (sta_vdev_id == info[i].vdev_id) { + *ch_freq = info[j].ch_freq; + return true; + } else if (sta_vdev_id == + info[j].vdev_id) { + *ch_freq = info[j].ch_freq; + return true; + } + } + } + } + + return false; + default: + policy_mgr_debug("TDLS off channel not allowed on > 3 port conc"); + break; + } + + return false; +} +#endif diff --git a/components/tdls/core/src/wlan_tdls_ct.c b/components/tdls/core/src/wlan_tdls_ct.c index 2eb414e9bb..141b8b74f8 100644 --- a/components/tdls/core/src/wlan_tdls_ct.c +++ b/components/tdls/core/src/wlan_tdls_ct.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023 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 @@ -28,6 +28,7 @@ #include "wlan_tdls_ct.h" #include "wlan_tdls_cmds_process.h" #include "wlan_reg_services_api.h" +#include "wlan_policy_mgr_api.h" bool tdls_is_vdev_authenticated(struct wlan_objmgr_vdev *vdev) { @@ -1044,11 +1045,117 @@ int tdls_set_tdls_secoffchanneloffset(struct tdls_soc_priv_obj *tdls_soc, return 0; } +static inline void +tdls_update_opclass(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *params) +{ + params->oper_class = tdls_find_opclass(psoc, params->tdls_off_ch, + params->tdls_off_ch_bw_offset); + if (params->oper_class) + return; + + if (params->tdls_off_ch_bw_offset == BW40_HIGH_PRIMARY) + params->oper_class = tdls_find_opclass(psoc, + params->tdls_off_ch, + BW40_LOW_PRIMARY); + else if (params->tdls_off_ch_bw_offset == BW40_LOW_PRIMARY) + params->oper_class = tdls_find_opclass(psoc, + params->tdls_off_ch, + BW40_HIGH_PRIMARY); +} + +#ifdef WLAN_FEATURE_TDLS_CONCURRENCIES +static inline QDF_STATUS +tdls_update_peer_off_channel_list(struct wlan_objmgr_pdev *pdev, + struct tdls_soc_priv_obj *tdls_soc, + struct wlan_objmgr_vdev *vdev, + struct tdls_peer *peer, + struct tdls_channel_switch_params *params) +{ + struct wlan_objmgr_psoc *psoc = wlan_pdev_get_psoc(pdev); + struct tdls_peer_update_state *peer_info; + struct tdls_ch_params *off_channels = params->allowed_off_channels; + uint16_t i; + qdf_freq_t freq, peer_freq; + + if (!wlan_psoc_nif_fw_ext2_cap_get(psoc, + WLAN_TDLS_CONCURRENCIES_SUPPORT)) + return false; + + if (!policy_mgr_get_allowed_tdls_offchannel_freq(psoc, vdev, &freq)) { + tdls_debug("off channel not allowed for current concurrency"); + return false; + } + + /* + * Overwrite the preferred off channel freq in case of concurrency + */ + if (freq) { + params->tdls_off_ch = wlan_reg_freq_to_chan(pdev, freq); + params->tdls_off_chan_freq = freq; + + /* + * tdls_off_ch_bw_offset is already filled in the caller + */ + if (tdls_soc->tdls_off_channel && + tdls_soc->tdls_channel_offset != BW_INVALID) { + tdls_update_opclass(psoc, params); + } else if (peer->off_channel_capable && + peer->pref_off_chan_freq) { + params->oper_class = + tdls_get_opclass_from_bandwidth( + vdev, params->tdls_off_chan_freq, + peer->pref_off_chan_width, + ¶ms->tdls_off_ch_bw_offset); + } + } + + peer_info = qdf_mem_malloc(sizeof(*peer_info)); + if (!peer_info) + return QDF_STATUS_E_NOMEM; + + tdls_extract_peer_state_param(peer_info, peer); + params->num_off_channels = 0; + + /* + * If TDLS concurrency is supported and freq == 0, + * then allow all the 5GHz and 6GHz peer supported frequencies for + * off-channel operation. If particular frequency is provided based on + * concurrency combination then only allow that channel for off-channel. + */ + for (i = 0; i < peer_info->peer_cap.peer_chanlen; i++) { + peer_freq = peer_info->peer_cap.peer_chan[i].ch_freq; + if ((!freq || freq == peer_freq) && + (!wlan_reg_is_24ghz_ch_freq(peer_freq) || + (wlan_reg_is_6ghz_chan_freq(peer_freq) && + tdls_is_6g_freq_allowed(vdev, peer_freq)))) { + off_channels[i] = peer_info->peer_cap.peer_chan[i]; + params->num_off_channels++; + } + } + tdls_debug("Num allowed off channels:%d freq:%u", + params->num_off_channels, freq); + qdf_mem_free(peer_info); + + return QDF_STATUS_SUCCESS; +} +#else +static inline QDF_STATUS +tdls_update_peer_off_channel_list(struct wlan_objmgr_pdev *pdev, + struct tdls_soc_priv_obj *tdls_soc, + struct wlan_objmgr_vdev *vdev, + struct tdls_peer *peer, + struct tdls_channel_switch_params *params) +{ + return QDF_STATUS_SUCCESS; +} +#endif + int tdls_set_tdls_offchannelmode(struct wlan_objmgr_vdev *vdev, int offchanmode) { struct tdls_peer *conn_peer = NULL; - struct tdls_channel_switch_params chan_switch_params = {0}; + struct tdls_channel_switch_params *chan_switch_params; QDF_STATUS status = QDF_STATUS_E_FAILURE; int ret_value = 0; struct tdls_vdev_priv_obj *tdls_vdev; @@ -1063,7 +1170,7 @@ int tdls_set_tdls_offchannelmode(struct wlan_objmgr_vdev *vdev, if (offchanmode < ENABLE_CHANSWITCH || - offchanmode > DISABLE_CHANSWITCH) { + offchanmode > DISABLE_ACTIVE_CHANSWITCH) { tdls_err("Invalid tdls off channel mode %d", offchanmode); return -EINVAL; } @@ -1091,103 +1198,92 @@ int tdls_set_tdls_offchannelmode(struct wlan_objmgr_vdev *vdev, offchanmode, tdls_soc->tdls_off_channel, tdls_soc->tdls_channel_offset); + chan_switch_params = qdf_mem_malloc(sizeof(*chan_switch_params)); + if (!chan_switch_params) + return -ENOMEM; + switch (offchanmode) { case ENABLE_CHANSWITCH: if (tdls_soc->tdls_off_channel && - tdls_soc->tdls_channel_offset != BW_INVALID) { - chan_switch_params.tdls_off_ch = - tdls_soc->tdls_off_channel; - chan_switch_params.tdls_off_ch_bw_offset = - tdls_soc->tdls_channel_offset; - chan_switch_params.oper_class = - tdls_find_opclass(tdls_soc->soc, - chan_switch_params.tdls_off_ch, - chan_switch_params.tdls_off_ch_bw_offset); - if (!chan_switch_params.oper_class) { - if (chan_switch_params.tdls_off_ch_bw_offset == - BW40_HIGH_PRIMARY) - chan_switch_params.oper_class = - tdls_find_opclass(tdls_soc->soc, - chan_switch_params.tdls_off_ch, - BW40_LOW_PRIMARY); - else if (chan_switch_params. - tdls_off_ch_bw_offset == - BW40_LOW_PRIMARY) - chan_switch_params.oper_class = - tdls_find_opclass(tdls_soc->soc, - chan_switch_params.tdls_off_ch, - BW40_HIGH_PRIMARY); - tdls_debug("oper_class:%d", - chan_switch_params.oper_class); - } + tdls_soc->tdls_channel_offset != BW_INVALID) { + chan_switch_params->tdls_off_ch = + tdls_soc->tdls_off_channel; + chan_switch_params->tdls_off_ch_bw_offset = + tdls_soc->tdls_channel_offset; + tdls_update_opclass(wlan_pdev_get_psoc(pdev), + chan_switch_params); } else if (conn_peer->off_channel_capable && conn_peer->pref_off_chan_freq) { - chan_switch_params.tdls_off_ch = + chan_switch_params->tdls_off_ch = wlan_reg_freq_to_chan(pdev, conn_peer->pref_off_chan_freq); - chan_switch_params.oper_class = + chan_switch_params->oper_class = tdls_get_opclass_from_bandwidth( vdev, conn_peer->pref_off_chan_freq, conn_peer->pref_off_chan_width, - &chan_switch_params.tdls_off_ch_bw_offset); - chan_switch_params.tdls_off_chan_freq = + &chan_switch_params->tdls_off_ch_bw_offset); + chan_switch_params->tdls_off_chan_freq = conn_peer->pref_off_chan_freq; } else { tdls_err("TDLS off-channel parameters are not set yet!!!"); + qdf_mem_free(chan_switch_params); return -EINVAL; } + + /* + * Retain the connected peer preferred off-channel frequency + * and opclass that was calculated during update peer caps and + * don't overwrite it based on concurrency in + * tdls_update_peer_off_channel_list(). + */ + conn_peer->pref_off_chan_freq = + wlan_reg_chan_opclass_to_freq( + chan_switch_params->tdls_off_ch, + chan_switch_params->oper_class, false); + conn_peer->op_class_for_pref_off_chan = + chan_switch_params->oper_class; + + tdls_update_peer_off_channel_list(pdev, tdls_soc, vdev, + conn_peer, + chan_switch_params); break; case DISABLE_CHANSWITCH: - chan_switch_params.tdls_off_ch = 0; - chan_switch_params.tdls_off_ch_bw_offset = 0; - chan_switch_params.oper_class = 0; + case DISABLE_ACTIVE_CHANSWITCH: + chan_switch_params->tdls_off_ch = 0; + chan_switch_params->tdls_off_ch_bw_offset = 0; + chan_switch_params->oper_class = 0; break; default: tdls_err("Incorrect Parameters mode: %d tdls_off_channel: %d offchanoffset: %d", - offchanmode, tdls_soc->tdls_off_channel, - tdls_soc->tdls_channel_offset); + offchanmode, tdls_soc->tdls_off_channel, + tdls_soc->tdls_channel_offset); + qdf_mem_free(chan_switch_params); return -EINVAL; } /* end switch */ - chan_switch_params.vdev_id = tdls_vdev->session_id; - chan_switch_params.tdls_sw_mode = offchanmode; - chan_switch_params.is_responder = - conn_peer->is_responder; - qdf_mem_copy(&chan_switch_params.peer_mac_addr, - &conn_peer->peer_mac.bytes, - QDF_MAC_ADDR_SIZE); + chan_switch_params->vdev_id = tdls_vdev->session_id; + chan_switch_params->tdls_sw_mode = offchanmode; + chan_switch_params->is_responder = conn_peer->is_responder; + qdf_mem_copy(&chan_switch_params->peer_mac_addr, + &conn_peer->peer_mac.bytes, QDF_MAC_ADDR_SIZE); tdls_notice("Peer " QDF_MAC_ADDR_FMT " vdevId: %d, off channel: %d, offset: %d, mode: %d, is_responder: %d", - QDF_MAC_ADDR_REF(chan_switch_params.peer_mac_addr), - chan_switch_params.vdev_id, - chan_switch_params.tdls_off_ch, - chan_switch_params.tdls_off_ch_bw_offset, - chan_switch_params.tdls_sw_mode, - chan_switch_params.is_responder); + QDF_MAC_ADDR_REF(chan_switch_params->peer_mac_addr), + chan_switch_params->vdev_id, + chan_switch_params->tdls_off_ch, + chan_switch_params->tdls_off_ch_bw_offset, + chan_switch_params->tdls_sw_mode, + chan_switch_params->is_responder); - status = tdls_set_offchan_mode(tdls_soc->soc, - &chan_switch_params); - - if (status != QDF_STATUS_SUCCESS) { + status = tdls_set_offchan_mode(tdls_soc->soc, chan_switch_params); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(chan_switch_params); tdls_err("Failed to send channel switch request to wmi"); return -EINVAL; } tdls_soc->tdls_fw_off_chan_mode = offchanmode; - - if (ENABLE_CHANSWITCH == offchanmode) { - conn_peer = tdls_find_first_connected_peer(tdls_vdev); - if (!conn_peer) { - tdls_err("No TDLS Connected Peer"); - return -EPERM; - } - conn_peer->pref_off_chan_freq = wlan_reg_chan_opclass_to_freq( - chan_switch_params.tdls_off_ch, - chan_switch_params.oper_class, - false); - conn_peer->op_class_for_pref_off_chan = - chan_switch_params.oper_class; - } + qdf_mem_free(chan_switch_params); return ret_value; } @@ -1318,8 +1414,7 @@ void tdls_disable_offchan_and_teardown_links( /* Send Msg to PE for deleting all the TDLS peers */ tdls_delete_all_tdls_peers(vdev, tdls_soc); - for (staidx = 0; staidx < tdls_soc->max_num_tdls_sta; - staidx++) { + for (staidx = 0; staidx < tdls_soc->max_num_tdls_sta; staidx++) { if (!tdls_soc->tdls_conn_info[staidx].valid_entry) continue; @@ -1332,8 +1427,7 @@ void tdls_disable_offchan_and_teardown_links( QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes)); /* Indicate teardown to supplicant */ - tdls_indicate_teardown(tdls_vdev, - curr_peer, + tdls_indicate_teardown(tdls_vdev, curr_peer, TDLS_TEARDOWN_PEER_UNSPEC_REASON); /* diff --git a/components/tdls/dispatcher/inc/wlan_tdls_public_structs.h b/components/tdls/dispatcher/inc/wlan_tdls_public_structs.h index 1e70d67e53..527a13d38a 100644 --- a/components/tdls/dispatcher/inc/wlan_tdls_public_structs.h +++ b/components/tdls/dispatcher/inc/wlan_tdls_public_structs.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023 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 @@ -45,8 +45,11 @@ #define WLAN_MAX_SUPP_OPER_CLASSES 32 #define WLAN_MAC_MAX_SUPP_RATES 32 #define WLAN_CHANNEL_14 14 + #define ENABLE_CHANSWITCH 1 #define DISABLE_CHANSWITCH 2 +#define DISABLE_ACTIVE_CHANSWITCH 3 + #define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_MIN 1 #define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX 165 #define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF 36 @@ -1008,6 +1011,8 @@ struct tdls_peer_update_state { * @oper_class: Operating class for target channel * @is_responder: Responder or initiator * @tdls_off_chan_freq: Target Off Channel frequency + * @num_off_channels: Number of channels allowed for off channel operation + * @allowed_off_channels: Channel list allowed for off channels */ struct tdls_channel_switch_params { uint32_t vdev_id; @@ -1018,6 +1023,8 @@ struct tdls_channel_switch_params { uint8_t oper_class; uint8_t is_responder; uint32_t tdls_off_chan_freq; + uint16_t num_off_channels; + struct tdls_ch_params allowed_off_channels[WLAN_MAC_WMI_MAX_SUPP_CHANNELS]; }; /**