qcacld-3.0: Add support to disable TDLS offchannel completely

Add support for new TDLS off-channel mode type:
DISABLE_ACTIVE_CHANSWITCH. With this
off channel mode, the TDLS off-channel will be disabled
completely without passive disable mode.
DISABLE_CHANSWITCH -> Passive channel switch can be done i.e if
peer requests channel switch then firmware can do channel switch
DISABLE_ACTIVE_CHANSWITCH -> Disable all off-channel switches.

Add support to send peer channel lists based on concurrency
combination. Override the ini configured frequency to the
supported frequency based on the current concurrency.

Change-Id: Ie3210178eb8b57d6ab126a730ed91895b70edaa1
CRs-Fixed: 3416213
This commit is contained in:
Pragaspathi Thilagaraj
2023-02-14 17:37:29 +05:30
committed by Madan Koyyalamudi
parent 9d72a8fc92
commit 06be2cbc56
4 changed files with 313 additions and 74 deletions

View File

@@ -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, bool policy_mgr_is_sap_allowed_on_indoor(struct wlan_objmgr_pdev *pdev,
uint8_t vdev_id, qdf_freq_t ch_freq); 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 */ #endif /* __WLAN_POLICY_MGR_API_H */

View File

@@ -3543,3 +3543,122 @@ ret_value:
return 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

View File

@@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. * 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 * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * any purpose with or without fee is hereby granted, provided that the
@@ -28,6 +28,7 @@
#include "wlan_tdls_ct.h" #include "wlan_tdls_ct.h"
#include "wlan_tdls_cmds_process.h" #include "wlan_tdls_cmds_process.h"
#include "wlan_reg_services_api.h" #include "wlan_reg_services_api.h"
#include "wlan_policy_mgr_api.h"
bool tdls_is_vdev_authenticated(struct wlan_objmgr_vdev *vdev) 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; 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,
&params->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 tdls_set_tdls_offchannelmode(struct wlan_objmgr_vdev *vdev,
int offchanmode) int offchanmode)
{ {
struct tdls_peer *conn_peer = NULL; 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; QDF_STATUS status = QDF_STATUS_E_FAILURE;
int ret_value = 0; int ret_value = 0;
struct tdls_vdev_priv_obj *tdls_vdev; 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 || if (offchanmode < ENABLE_CHANSWITCH ||
offchanmode > DISABLE_CHANSWITCH) { offchanmode > DISABLE_ACTIVE_CHANSWITCH) {
tdls_err("Invalid tdls off channel mode %d", offchanmode); tdls_err("Invalid tdls off channel mode %d", offchanmode);
return -EINVAL; return -EINVAL;
} }
@@ -1091,103 +1198,92 @@ int tdls_set_tdls_offchannelmode(struct wlan_objmgr_vdev *vdev,
offchanmode, tdls_soc->tdls_off_channel, offchanmode, tdls_soc->tdls_off_channel,
tdls_soc->tdls_channel_offset); tdls_soc->tdls_channel_offset);
chan_switch_params = qdf_mem_malloc(sizeof(*chan_switch_params));
if (!chan_switch_params)
return -ENOMEM;
switch (offchanmode) { switch (offchanmode) {
case ENABLE_CHANSWITCH: case ENABLE_CHANSWITCH:
if (tdls_soc->tdls_off_channel && if (tdls_soc->tdls_off_channel &&
tdls_soc->tdls_channel_offset != BW_INVALID) { tdls_soc->tdls_channel_offset != BW_INVALID) {
chan_switch_params.tdls_off_ch = chan_switch_params->tdls_off_ch =
tdls_soc->tdls_off_channel; tdls_soc->tdls_off_channel;
chan_switch_params.tdls_off_ch_bw_offset = chan_switch_params->tdls_off_ch_bw_offset =
tdls_soc->tdls_channel_offset; tdls_soc->tdls_channel_offset;
chan_switch_params.oper_class = tdls_update_opclass(wlan_pdev_get_psoc(pdev),
tdls_find_opclass(tdls_soc->soc, chan_switch_params);
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);
}
} else if (conn_peer->off_channel_capable && } else if (conn_peer->off_channel_capable &&
conn_peer->pref_off_chan_freq) { conn_peer->pref_off_chan_freq) {
chan_switch_params.tdls_off_ch = chan_switch_params->tdls_off_ch =
wlan_reg_freq_to_chan(pdev, wlan_reg_freq_to_chan(pdev,
conn_peer->pref_off_chan_freq); conn_peer->pref_off_chan_freq);
chan_switch_params.oper_class = chan_switch_params->oper_class =
tdls_get_opclass_from_bandwidth( tdls_get_opclass_from_bandwidth(
vdev, conn_peer->pref_off_chan_freq, vdev, conn_peer->pref_off_chan_freq,
conn_peer->pref_off_chan_width, conn_peer->pref_off_chan_width,
&chan_switch_params.tdls_off_ch_bw_offset); &chan_switch_params->tdls_off_ch_bw_offset);
chan_switch_params.tdls_off_chan_freq = chan_switch_params->tdls_off_chan_freq =
conn_peer->pref_off_chan_freq; conn_peer->pref_off_chan_freq;
} else { } else {
tdls_err("TDLS off-channel parameters are not set yet!!!"); tdls_err("TDLS off-channel parameters are not set yet!!!");
qdf_mem_free(chan_switch_params);
return -EINVAL; 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; break;
case DISABLE_CHANSWITCH: case DISABLE_CHANSWITCH:
chan_switch_params.tdls_off_ch = 0; case DISABLE_ACTIVE_CHANSWITCH:
chan_switch_params.tdls_off_ch_bw_offset = 0; chan_switch_params->tdls_off_ch = 0;
chan_switch_params.oper_class = 0; chan_switch_params->tdls_off_ch_bw_offset = 0;
chan_switch_params->oper_class = 0;
break; break;
default: default:
tdls_err("Incorrect Parameters mode: %d tdls_off_channel: %d offchanoffset: %d", tdls_err("Incorrect Parameters mode: %d tdls_off_channel: %d offchanoffset: %d",
offchanmode, tdls_soc->tdls_off_channel, offchanmode, tdls_soc->tdls_off_channel,
tdls_soc->tdls_channel_offset); tdls_soc->tdls_channel_offset);
qdf_mem_free(chan_switch_params);
return -EINVAL; return -EINVAL;
} /* end switch */ } /* end switch */
chan_switch_params.vdev_id = tdls_vdev->session_id; chan_switch_params->vdev_id = tdls_vdev->session_id;
chan_switch_params.tdls_sw_mode = offchanmode; chan_switch_params->tdls_sw_mode = offchanmode;
chan_switch_params.is_responder = chan_switch_params->is_responder = conn_peer->is_responder;
conn_peer->is_responder; qdf_mem_copy(&chan_switch_params->peer_mac_addr,
qdf_mem_copy(&chan_switch_params.peer_mac_addr, &conn_peer->peer_mac.bytes, QDF_MAC_ADDR_SIZE);
&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", 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), QDF_MAC_ADDR_REF(chan_switch_params->peer_mac_addr),
chan_switch_params.vdev_id, chan_switch_params->vdev_id,
chan_switch_params.tdls_off_ch, chan_switch_params->tdls_off_ch,
chan_switch_params.tdls_off_ch_bw_offset, chan_switch_params->tdls_off_ch_bw_offset,
chan_switch_params.tdls_sw_mode, chan_switch_params->tdls_sw_mode,
chan_switch_params.is_responder); chan_switch_params->is_responder);
status = tdls_set_offchan_mode(tdls_soc->soc, status = tdls_set_offchan_mode(tdls_soc->soc, chan_switch_params);
&chan_switch_params); if (QDF_IS_STATUS_ERROR(status)) {
qdf_mem_free(chan_switch_params);
if (status != QDF_STATUS_SUCCESS) {
tdls_err("Failed to send channel switch request to wmi"); tdls_err("Failed to send channel switch request to wmi");
return -EINVAL; return -EINVAL;
} }
tdls_soc->tdls_fw_off_chan_mode = offchanmode; tdls_soc->tdls_fw_off_chan_mode = offchanmode;
qdf_mem_free(chan_switch_params);
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;
}
return ret_value; return ret_value;
} }
@@ -1318,8 +1414,7 @@ void tdls_disable_offchan_and_teardown_links(
/* Send Msg to PE for deleting all the TDLS peers */ /* Send Msg to PE for deleting all the TDLS peers */
tdls_delete_all_tdls_peers(vdev, tdls_soc); tdls_delete_all_tdls_peers(vdev, tdls_soc);
for (staidx = 0; staidx < tdls_soc->max_num_tdls_sta; for (staidx = 0; staidx < tdls_soc->max_num_tdls_sta; staidx++) {
staidx++) {
if (!tdls_soc->tdls_conn_info[staidx].valid_entry) if (!tdls_soc->tdls_conn_info[staidx].valid_entry)
continue; continue;
@@ -1332,8 +1427,7 @@ void tdls_disable_offchan_and_teardown_links(
QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes)); QDF_MAC_ADDR_REF(curr_peer->peer_mac.bytes));
/* Indicate teardown to supplicant */ /* Indicate teardown to supplicant */
tdls_indicate_teardown(tdls_vdev, tdls_indicate_teardown(tdls_vdev, curr_peer,
curr_peer,
TDLS_TEARDOWN_PEER_UNSPEC_REASON); TDLS_TEARDOWN_PEER_UNSPEC_REASON);
/* /*

View File

@@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. * 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 * Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the * 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_MAX_SUPP_OPER_CLASSES 32
#define WLAN_MAC_MAX_SUPP_RATES 32 #define WLAN_MAC_MAX_SUPP_RATES 32
#define WLAN_CHANNEL_14 14 #define WLAN_CHANNEL_14 14
#define ENABLE_CHANSWITCH 1 #define ENABLE_CHANSWITCH 1
#define DISABLE_CHANSWITCH 2 #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_MIN 1
#define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX 165 #define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX 165
#define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF 36 #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 * @oper_class: Operating class for target channel
* @is_responder: Responder or initiator * @is_responder: Responder or initiator
* @tdls_off_chan_freq: Target Off Channel frequency * @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 { struct tdls_channel_switch_params {
uint32_t vdev_id; uint32_t vdev_id;
@@ -1018,6 +1023,8 @@ struct tdls_channel_switch_params {
uint8_t oper_class; uint8_t oper_class;
uint8_t is_responder; uint8_t is_responder;
uint32_t tdls_off_chan_freq; uint32_t tdls_off_chan_freq;
uint16_t num_off_channels;
struct tdls_ch_params allowed_off_channels[WLAN_MAC_WMI_MAX_SUPP_CHANNELS];
}; };
/** /**