mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-10 09:09:55 -04:00
wifi: iwlwifi: mld: support channel survey collection for ACS scans
The firmware is able to collect channel statistics when doing passive scans. Enable the flag when doing a passive scan on an AP interface and collect the survey information. Signed-off-by: Benjamin Berg <benjamin.berg@intel.com> Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com> Link: https://patch.msgid.link/20250723094230.a659ef1b1fd8.I24a9a0383327c231f36be170968bc7bac801f9f2@changeid
This commit is contained in:
committed by
Miri Korenblit
parent
fc3475fa46
commit
0dd86ab21d
@@ -2675,6 +2675,7 @@ const struct ieee80211_ops iwl_mld_hw_ops = {
|
||||
.mgd_complete_tx = iwl_mld_mac_mgd_complete_tx,
|
||||
.sta_state = iwl_mld_mac80211_sta_state,
|
||||
.sta_statistics = iwl_mld_mac80211_sta_statistics,
|
||||
.get_survey = iwl_mld_mac80211_get_survey,
|
||||
.flush = iwl_mld_mac80211_flush,
|
||||
.flush_sta = iwl_mld_mac80211_flush_sta,
|
||||
.ampdu_action = iwl_mld_mac80211_ampdu_action,
|
||||
|
||||
@@ -259,6 +259,13 @@ static const struct iwl_hcmd_names iwl_mld_data_path_names[] = {
|
||||
HCMD_NAME(MU_GROUP_MGMT_NOTIF),
|
||||
};
|
||||
|
||||
/* Please keep this array *SORTED* by hex value.
|
||||
* Access is done through binary search
|
||||
*/
|
||||
static const struct iwl_hcmd_names iwl_mld_scan_names[] = {
|
||||
HCMD_NAME(CHANNEL_SURVEY_NOTIF),
|
||||
};
|
||||
|
||||
/* Please keep this array *SORTED* by hex value.
|
||||
* Access is done through binary search
|
||||
*/
|
||||
@@ -310,6 +317,7 @@ const struct iwl_hcmd_arr iwl_mld_groups[] = {
|
||||
[SYSTEM_GROUP] = HCMD_ARR(iwl_mld_system_names),
|
||||
[MAC_CONF_GROUP] = HCMD_ARR(iwl_mld_mac_conf_names),
|
||||
[DATA_PATH_GROUP] = HCMD_ARR(iwl_mld_data_path_names),
|
||||
[SCAN_GROUP] = HCMD_ARR(iwl_mld_scan_names),
|
||||
[LOCATION_GROUP] = HCMD_ARR(iwl_mld_location_names),
|
||||
[REGULATORY_AND_NVM_GROUP] = HCMD_ARR(iwl_mld_reg_and_nvm_names),
|
||||
[DEBUG_GROUP] = HCMD_ARR(iwl_mld_debug_names),
|
||||
@@ -507,6 +515,7 @@ iwl_op_mode_mld_stop(struct iwl_op_mode *op_mode)
|
||||
|
||||
kfree(mld->nvm_data);
|
||||
kfree(mld->scan.cmd);
|
||||
kfree(mld->channel_survey);
|
||||
kfree(mld->error_recovery_buf);
|
||||
kfree(mld->mcast_filter_cmd);
|
||||
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
* device
|
||||
* @addresses: device MAC addresses.
|
||||
* @scan: instance of the scan object
|
||||
* @channel_survey: channel survey information collected during scan
|
||||
* @wowlan: WoWLAN support data.
|
||||
* @debug_max_sleep: maximum sleep time in D3 (for debug purposes)
|
||||
* @led: the led device
|
||||
@@ -253,6 +254,7 @@ struct iwl_mld {
|
||||
|
||||
struct mac_address addresses[IWL_MLD_MAX_ADDRESSES];
|
||||
struct iwl_mld_scan scan;
|
||||
struct iwl_mld_survey *channel_survey;
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
struct wiphy_wowlan_support wowlan;
|
||||
u32 debug_max_sleep;
|
||||
|
||||
@@ -295,6 +295,8 @@ CMD_VERSIONS(scan_complete_notif,
|
||||
CMD_VER_ENTRY(1, iwl_umac_scan_complete))
|
||||
CMD_VERSIONS(scan_iter_complete_notif,
|
||||
CMD_VER_ENTRY(2, iwl_umac_scan_iter_complete_notif))
|
||||
CMD_VERSIONS(channel_survey_notif,
|
||||
CMD_VER_ENTRY(1, iwl_umac_scan_channel_survey_notif))
|
||||
CMD_VERSIONS(mfuart_notif,
|
||||
CMD_VER_ENTRY(2, iwl_mfuart_load_notif))
|
||||
CMD_VERSIONS(update_mcc,
|
||||
@@ -415,6 +417,10 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = {
|
||||
RX_HANDLER_NO_VAL(LEGACY_GROUP, MATCH_FOUND_NOTIFICATION,
|
||||
match_found_notif, RX_HANDLER_SYNC)
|
||||
|
||||
RX_HANDLER_NO_OBJECT(SCAN_GROUP, CHANNEL_SURVEY_NOTIF,
|
||||
channel_survey_notif,
|
||||
RX_HANDLER_ASYNC)
|
||||
|
||||
RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_NOTIF,
|
||||
stats_oper_notif, RX_HANDLER_ASYNC)
|
||||
RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF,
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
*/
|
||||
#include <linux/crc32.h>
|
||||
|
||||
#include "iwl-utils.h"
|
||||
|
||||
#include "mld.h"
|
||||
#include "scan.h"
|
||||
#include "hcmd.h"
|
||||
@@ -482,7 +484,9 @@ iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld,
|
||||
static u8
|
||||
iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld,
|
||||
struct iwl_mld_scan_params *params,
|
||||
struct ieee80211_vif *vif, u16 gen_flags)
|
||||
struct ieee80211_vif *vif,
|
||||
enum iwl_mld_scan_status scan_status,
|
||||
u16 gen_flags)
|
||||
{
|
||||
u8 flags = 0;
|
||||
|
||||
@@ -494,6 +498,17 @@ iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld,
|
||||
if (params->scan_6ghz)
|
||||
flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_DONT_TOGGLE_ANT;
|
||||
|
||||
/* For AP interfaces, request survey data for regular scans and if
|
||||
* it is supported. For non-AP interfaces, EBS will be enabled and
|
||||
* the results may be missing information for some channels.
|
||||
*/
|
||||
if (scan_status == IWL_MLD_SCAN_REGULAR &&
|
||||
ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP &&
|
||||
gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FORCE_PASSIVE &&
|
||||
iwl_fw_lookup_notif_ver(mld->fw, SCAN_GROUP,
|
||||
CHANNEL_SURVEY_NOTIF, 0) >= 1)
|
||||
flags |= IWL_UMAC_SCAN_GEN_FLAGS2_COLLECT_CHANNEL_STATS;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
@@ -544,6 +559,7 @@ iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld,
|
||||
u16 gen_flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif,
|
||||
scan_status);
|
||||
u8 gen_flags2 = iwl_mld_scan_get_cmd_gen_flags2(mld, params, vif,
|
||||
scan_status,
|
||||
gen_flags);
|
||||
|
||||
IWL_DEBUG_SCAN(mld, "General: flags=0x%x, flags2=0x%x\n",
|
||||
@@ -1752,6 +1768,11 @@ int iwl_mld_regular_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
struct cfg80211_scan_request *req,
|
||||
struct ieee80211_scan_ies *ies)
|
||||
{
|
||||
/* Clear survey data when starting the first part of a regular scan */
|
||||
if (req->first_part && mld->channel_survey)
|
||||
memset(mld->channel_survey->channels, 0,
|
||||
sizeof(mld->channel_survey->channels[0]) *
|
||||
mld->channel_survey->n_channels);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
|
||||
iwl_mld_emlsr_block_tmp_non_bss(mld);
|
||||
@@ -2025,3 +2046,136 @@ int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwl_mld_chanidx_from_phy(struct iwl_mld *mld,
|
||||
enum nl80211_band band,
|
||||
u16 phy_chan_num)
|
||||
{
|
||||
struct ieee80211_supported_band *sband = mld->wiphy->bands[band];
|
||||
|
||||
if (WARN_ON_ONCE(!sband))
|
||||
return -EINVAL;
|
||||
|
||||
for (int chan_idx = 0; chan_idx < sband->n_channels; chan_idx++) {
|
||||
struct ieee80211_channel *channel = &sband->channels[chan_idx];
|
||||
|
||||
if (channel->hw_value == phy_chan_num)
|
||||
return chan_idx;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void iwl_mld_handle_channel_survey_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
const struct iwl_umac_scan_channel_survey_notif *notif =
|
||||
(void *)pkt->data;
|
||||
struct iwl_mld_survey_channel *info;
|
||||
enum nl80211_band band;
|
||||
int chan_idx;
|
||||
|
||||
if (!mld->channel_survey) {
|
||||
size_t n_channels = 0;
|
||||
|
||||
for (band = 0; band < NUM_NL80211_BANDS; band++) {
|
||||
if (!mld->wiphy->bands[band])
|
||||
continue;
|
||||
|
||||
n_channels += mld->wiphy->bands[band]->n_channels;
|
||||
}
|
||||
|
||||
mld->channel_survey = kzalloc(struct_size(mld->channel_survey,
|
||||
channels, n_channels),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!mld->channel_survey)
|
||||
return;
|
||||
|
||||
mld->channel_survey->n_channels = n_channels;
|
||||
n_channels = 0;
|
||||
for (band = 0; band < NUM_NL80211_BANDS; band++) {
|
||||
if (!mld->wiphy->bands[band])
|
||||
continue;
|
||||
|
||||
mld->channel_survey->bands[band] =
|
||||
&mld->channel_survey->channels[n_channels];
|
||||
n_channels += mld->wiphy->bands[band]->n_channels;
|
||||
}
|
||||
}
|
||||
|
||||
band = iwl_mld_phy_band_to_nl80211(le32_to_cpu(notif->band));
|
||||
chan_idx = iwl_mld_chanidx_from_phy(mld, band,
|
||||
le32_to_cpu(notif->channel));
|
||||
if (WARN_ON_ONCE(chan_idx < 0))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_SCAN(mld, "channel survey received for freq %d\n",
|
||||
mld->wiphy->bands[band]->channels[chan_idx].center_freq);
|
||||
|
||||
info = &mld->channel_survey->bands[band][chan_idx];
|
||||
|
||||
/* Times are all in ms */
|
||||
info->time = le32_to_cpu(notif->active_time);
|
||||
info->time_busy = le32_to_cpu(notif->busy_time);
|
||||
info->noise =
|
||||
iwl_average_neg_dbm(notif->noise, ARRAY_SIZE(notif->noise));
|
||||
}
|
||||
|
||||
int iwl_mld_mac80211_get_survey(struct ieee80211_hw *hw, int idx,
|
||||
struct survey_info *survey)
|
||||
{
|
||||
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
|
||||
int curr_idx = 0;
|
||||
|
||||
if (!mld->channel_survey)
|
||||
return -ENOENT;
|
||||
|
||||
/* Iterate bands/channels to find the requested index.
|
||||
* Logically this returns the entry with index "idx" from a flattened
|
||||
* survey result array that only contains channels with information.
|
||||
* The current index into this flattened array is tracked in curr_idx.
|
||||
*/
|
||||
for (enum nl80211_band band = 0; band < NUM_NL80211_BANDS; band++) {
|
||||
struct ieee80211_supported_band *sband =
|
||||
mld->wiphy->bands[band];
|
||||
|
||||
if (!sband)
|
||||
continue;
|
||||
|
||||
for (int per_band_idx = 0;
|
||||
per_band_idx < sband->n_channels;
|
||||
per_band_idx++) {
|
||||
struct iwl_mld_survey_channel *info =
|
||||
&mld->channel_survey->bands[band][per_band_idx];
|
||||
|
||||
/* Skip entry entirely, it was not reported/scanned,
|
||||
* do not increase curr_idx for this entry.
|
||||
*/
|
||||
if (!info->time)
|
||||
continue;
|
||||
|
||||
/* Search did not reach the requested entry yet,
|
||||
* increment curr_idx and continue.
|
||||
*/
|
||||
if (idx != curr_idx) {
|
||||
curr_idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Found (the next) channel to report */
|
||||
survey->channel = &sband->channels[per_band_idx];
|
||||
survey->filled = SURVEY_INFO_TIME |
|
||||
SURVEY_INFO_TIME_BUSY;
|
||||
survey->time = info->time;
|
||||
survey->time_busy = info->time_busy;
|
||||
survey->noise = info->noise;
|
||||
if (survey->noise < 0)
|
||||
survey->filled |= SURVEY_INFO_NOISE_DBM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,12 @@ void iwl_mld_handle_match_found_notif(struct iwl_mld *mld,
|
||||
void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
|
||||
int iwl_mld_mac80211_get_survey(struct ieee80211_hw *hw, int idx,
|
||||
struct survey_info *survey);
|
||||
|
||||
void iwl_mld_handle_channel_survey_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
|
||||
#define WFA_TPC_IE_LEN 9
|
||||
|
||||
static inline int iwl_mld_scan_max_template_size(void)
|
||||
@@ -133,4 +139,35 @@ struct iwl_mld_scan {
|
||||
u64 last_mlo_scan_time;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_mld_survey_channel - per-channel survey information
|
||||
*
|
||||
* Driver version of &struct survey_info with just the data we want to report.
|
||||
*
|
||||
* @time: time in ms the radio was on the channel
|
||||
* @time_busy: time in ms the channel was sensed busy
|
||||
* @noise: channel noise in dBm
|
||||
*/
|
||||
struct iwl_mld_survey_channel {
|
||||
u32 time;
|
||||
u32 time_busy;
|
||||
s8 noise;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_mld_survey - survey information
|
||||
*
|
||||
* Survey information for all available channels.
|
||||
*
|
||||
* @bands: per-band array for per-channel survey data, points into @channels
|
||||
* @n_channels: Number of @channels entries that are allocated
|
||||
* @channels: per-channel information
|
||||
*/
|
||||
struct iwl_mld_survey {
|
||||
struct iwl_mld_survey_channel *bands[NUM_NL80211_BANDS];
|
||||
|
||||
int n_channels;
|
||||
struct iwl_mld_survey_channel channels[] __counted_by(n_channels);
|
||||
};
|
||||
|
||||
#endif /* __iwl_mld_scan_h__ */
|
||||
|
||||
Reference in New Issue
Block a user