Skip to content

Commit

Permalink
wifi: iwlwifi: mvm: record and return channel survey information
Browse files Browse the repository at this point in the history
While doing a passive scan, the firmware will report per-channel survey
information. This information is primarily useful for hostapd when doing
an ACS (Automatic Channel Selection). Collect this information and add
it to the result set when getting the survey information.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240506095953.9287591a5999.I54a3f9f6480d3694e67eea1cb4f5853beace2780@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Benjamin Berg authored and Johannes Berg committed May 6, 2024
1 parent 9732088 commit fc61222
Show file tree
Hide file tree
Showing 6 changed files with 430 additions and 8 deletions.
71 changes: 67 additions & 4 deletions drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -5899,6 +5899,65 @@ void iwl_mvm_mac_flush_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mutex_unlock(&mvm->mutex);
}

static int iwl_mvm_mac_get_acs_survey(struct iwl_mvm *mvm, int idx,
struct survey_info *survey)
{
int chan_idx;
enum nl80211_band band;
int ret;

mutex_lock(&mvm->mutex);

if (!mvm->acs_survey) {
ret = -ENOENT;
goto out;
}

/* Find and return the next entry that has a non-zero active time */
for (band = 0; band < NUM_NL80211_BANDS; band++) {
struct ieee80211_supported_band *sband =
mvm->hw->wiphy->bands[band];

if (!sband)
continue;

for (chan_idx = 0; chan_idx < sband->n_channels; chan_idx++) {
struct iwl_mvm_acs_survey_channel *info =
&mvm->acs_survey->bands[band][chan_idx];

if (!info->time)
continue;

/* Found (the next) channel to report */
survey->channel = &sband->channels[chan_idx];
survey->filled = SURVEY_INFO_TIME |
SURVEY_INFO_TIME_BUSY |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_TX;
survey->time = info->time;
survey->time_busy = info->time_busy;
survey->time_rx = info->time_rx;
survey->time_tx = info->time_tx;
survey->noise = info->noise;
if (survey->noise < 0)
survey->filled |= SURVEY_INFO_NOISE_DBM;

/* Clear time so that channel is only reported once */
info->time = 0;

ret = 0;
goto out;
}
}

ret = -ENOENT;

out:
mutex_unlock(&mvm->mutex);

return ret;
}

int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey)
{
Expand All @@ -5911,14 +5970,18 @@ int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,

memset(survey, 0, sizeof(*survey));

/* only support global statistics right now */
if (idx != 0)
return -ENOENT;

if (!fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
return -ENOENT;

/*
* Return the beacon stats at index zero and pass on following indices
* to the function returning the full survey, most likely for ACS
* (Automatic Channel Selection).
*/
if (idx > 0)
return iwl_mvm_mac_get_acs_survey(mvm, idx - 1, survey);

mutex_lock(&mvm->mutex);

if (iwl_mvm_firmware_running(mvm)) {
Expand Down
35 changes: 35 additions & 0 deletions drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,35 @@ struct iwl_mei_scan_filter {
struct work_struct scan_work;
};

/**
* struct iwl_mvm_acs_survey_channel - per-channel survey information
*
* Stripped down version of &struct survey_info.
*
* @time: time in ms the radio was on the channel
* @time_busy: time in ms the channel was sensed busy
* @time_tx: time in ms spent transmitting data
* @time_rx: time in ms spent receiving data
* @noise: channel noise in dBm
*/
struct iwl_mvm_acs_survey_channel {
u32 time;
u32 time_busy;
u32 time_tx;
u32 time_rx;
s8 noise;
};

struct iwl_mvm_acs_survey {
struct iwl_mvm_acs_survey_channel *bands[NUM_NL80211_BANDS];

/* Overall number of channels */
int n_channels;

/* Storage space for per-channel information follows */
struct iwl_mvm_acs_survey_channel channels[] __counted_by(n_channels);
};

struct iwl_mvm {
/* for logger access */
struct device *dev;
Expand Down Expand Up @@ -1287,6 +1316,8 @@ struct iwl_mvm {

struct iwl_mei_scan_filter mei_scan_filter;

struct iwl_mvm_acs_survey *acs_survey;

bool statistics_clear;
};

Expand Down Expand Up @@ -2033,6 +2064,8 @@ unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf);
bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
const struct iwl_mvm_link_sel_data *a,
const struct iwl_mvm_link_sel_data *b);

s8 iwl_mvm_average_dbm_values(const struct iwl_umac_scan_channel_survey_notif *notif);
#endif

/* AP and IBSS */
Expand Down Expand Up @@ -2115,6 +2148,8 @@ int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm);
void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm);
void iwl_mvm_scan_timeout_wk(struct work_struct *work);
int iwl_mvm_int_mlo_scan(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
void iwl_mvm_rx_channel_survey_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb);

/* Scheduled scan */
void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm,
Expand Down
4 changes: 4 additions & 0 deletions drivers/net/wireless/intel/iwlwifi/mvm/ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,9 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
RX_HANDLER_GRP(MAC_CONF_GROUP, ROC_NOTIF,
iwl_mvm_rx_roc_notif, RX_HANDLER_SYNC,
struct iwl_roc_notif),
RX_HANDLER_GRP(SCAN_GROUP, CHANNEL_SURVEY_NOTIF,
iwl_mvm_rx_channel_survey_notif, RX_HANDLER_ASYNC_LOCKED,
struct iwl_umac_scan_channel_survey_notif),
};
#undef RX_HANDLER
#undef RX_HANDLER_GRP
Expand Down Expand Up @@ -1582,6 +1585,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
kfree(mvm->temp_nvm_data);
for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++)
kfree(mvm->nvm_sections[i].data);
kfree(mvm->acs_survey);

cancel_delayed_work_sync(&mvm->tcm.work);

Expand Down
Loading

0 comments on commit fc61222

Please sign in to comment.