289 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright (C) 2013-2014, 2018-2019, 2022-2024 Intel Corporation4 * Copyright (C) 2013-2014 Intel Mobile Communications GmbH5 */6#include "mvm.h"7 8/* For counting bound interfaces */9struct iwl_mvm_active_iface_iterator_data {10 struct ieee80211_vif *ignore_vif;11 struct ieee80211_sta *sta_vif_ap_sta;12 enum iwl_sf_state sta_vif_state;13 u32 num_active_macs;14};15 16/*17 * Count bound interfaces which are not p2p, besides data->ignore_vif.18 * data->station_vif will point to one bound vif of type station, if exists.19 */20static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac,21 struct ieee80211_vif *vif)22{23 struct iwl_mvm_active_iface_iterator_data *data = _data;24 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);25 26 if (vif == data->ignore_vif || !mvmvif->deflink.phy_ctxt ||27 vif->type == NL80211_IFTYPE_P2P_DEVICE)28 return;29 30 data->num_active_macs++;31 32 if (vif->type == NL80211_IFTYPE_STATION) {33 data->sta_vif_ap_sta = mvmvif->ap_sta;34 if (vif->cfg.assoc)35 data->sta_vif_state = SF_FULL_ON;36 else37 data->sta_vif_state = SF_INIT_OFF;38 }39}40 41/*42 * Aging and idle timeouts for the different possible scenarios43 * in default configuration44 */45static const46__le32 sf_full_timeout_def[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {47 {48 cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER_DEF),49 cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER_DEF)50 },51 {52 cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER_DEF),53 cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER_DEF)54 },55 {56 cpu_to_le32(SF_MCAST_AGING_TIMER_DEF),57 cpu_to_le32(SF_MCAST_IDLE_TIMER_DEF)58 },59 {60 cpu_to_le32(SF_BA_AGING_TIMER_DEF),61 cpu_to_le32(SF_BA_IDLE_TIMER_DEF)62 },63 {64 cpu_to_le32(SF_TX_RE_AGING_TIMER_DEF),65 cpu_to_le32(SF_TX_RE_IDLE_TIMER_DEF)66 },67};68 69/*70 * Aging and idle timeouts for the different possible scenarios71 * in single BSS MAC configuration.72 */73static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {74 {75 cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER),76 cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER)77 },78 {79 cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER),80 cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER)81 },82 {83 cpu_to_le32(SF_MCAST_AGING_TIMER),84 cpu_to_le32(SF_MCAST_IDLE_TIMER)85 },86 {87 cpu_to_le32(SF_BA_AGING_TIMER),88 cpu_to_le32(SF_BA_IDLE_TIMER)89 },90 {91 cpu_to_le32(SF_TX_RE_AGING_TIMER),92 cpu_to_le32(SF_TX_RE_IDLE_TIMER)93 },94};95 96static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm,97 struct iwl_sf_cfg_cmd *sf_cmd,98 struct ieee80211_sta *sta)99{100 int i, j, watermark;101 u8 max_rx_nss = 0;102 bool is_legacy = true;103 struct ieee80211_link_sta *link_sta;104 unsigned int link_id;105 106 sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN);107 108 /*109 * If we are in association flow - check antenna configuration110 * capabilities of the AP station, and choose the watermark accordingly.111 */112 if (sta) {113 /* find the maximal NSS number among all links (if relevant) */114 rcu_read_lock();115 for (link_id = 0; link_id < ARRAY_SIZE(sta->link); link_id++) {116 link_sta = rcu_dereference(sta->link[link_id]);117 if (!link_sta)118 continue;119 120 if (link_sta->ht_cap.ht_supported ||121 link_sta->vht_cap.vht_supported ||122 link_sta->eht_cap.has_eht ||123 link_sta->he_cap.has_he) {124 is_legacy = false;125 max_rx_nss = max(max_rx_nss, link_sta->rx_nss);126 }127 }128 rcu_read_unlock();129 130 if (!is_legacy) {131 switch (max_rx_nss) {132 case 1:133 watermark = SF_W_MARK_SISO;134 break;135 case 2:136 watermark = SF_W_MARK_MIMO2;137 break;138 default:139 watermark = SF_W_MARK_MIMO3;140 break;141 }142 } else {143 watermark = SF_W_MARK_LEGACY;144 }145 /* default watermark value for unassociated mode. */146 } else {147 watermark = SF_W_MARK_MIMO2;148 }149 sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark);150 151 for (i = 0; i < SF_NUM_SCENARIO; i++) {152 for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) {153 sf_cmd->long_delay_timeouts[i][j] =154 cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);155 }156 }157 158 if (sta) {159 BUILD_BUG_ON(sizeof(sf_full_timeout) !=160 sizeof(__le32) * SF_NUM_SCENARIO *161 SF_NUM_TIMEOUT_TYPES);162 163 memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,164 sizeof(sf_full_timeout));165 } else {166 BUILD_BUG_ON(sizeof(sf_full_timeout_def) !=167 sizeof(__le32) * SF_NUM_SCENARIO *168 SF_NUM_TIMEOUT_TYPES);169 170 memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def,171 sizeof(sf_full_timeout_def));172 }173}174 175static int iwl_mvm_sf_config(struct iwl_mvm *mvm, struct ieee80211_sta *sta,176 enum iwl_sf_state new_state)177{178 struct iwl_sf_cfg_cmd sf_cmd = {179 .state = cpu_to_le32(new_state),180 };181 int ret = 0;182 183 /*184 * If an associated AP sta changed its antenna configuration, the state185 * will remain FULL_ON but SF parameters need to be reconsidered.186 */187 if (new_state != SF_FULL_ON && mvm->sf_state == new_state)188 return 0;189 190 switch (new_state) {191 case SF_UNINIT:192 iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);193 break;194 case SF_FULL_ON:195 if (!sta) {196 IWL_ERR(mvm,197 "No station: Cannot switch SF to FULL_ON\n");198 return -EINVAL;199 }200 iwl_mvm_fill_sf_command(mvm, &sf_cmd, sta);201 break;202 case SF_INIT_OFF:203 iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);204 break;205 default:206 WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",207 new_state);208 return -EINVAL;209 }210 211 ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC,212 sizeof(sf_cmd), &sf_cmd);213 if (!ret)214 mvm->sf_state = new_state;215 216 return ret;217}218 219/*220 * Update Smart fifo:221 * Count bound interfaces that are not to be removed, ignoring p2p devices,222 * and set new state accordingly.223 */224int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,225 bool remove_vif)226{227 enum iwl_sf_state new_state;228 struct iwl_mvm_vif *mvmvif = NULL;229 struct iwl_mvm_active_iface_iterator_data data = {230 .ignore_vif = changed_vif,231 .sta_vif_state = SF_UNINIT,232 };233 struct ieee80211_sta *sta = NULL;234 235 if (fw_has_api(&mvm->fw->ucode_capa,236 IWL_UCODE_TLV_API_SMART_FIFO_OFFLOAD))237 return 0;238 /*239 * Ignore the call if we are in HW Restart flow, or if the handled240 * vif is a p2p device.241 */242 if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||243 (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE))244 return 0;245 246 ieee80211_iterate_active_interfaces_atomic(mvm->hw,247 IEEE80211_IFACE_ITER_NORMAL,248 iwl_mvm_bound_iface_iterator,249 &data);250 251 /* If changed_vif exists and is not to be removed, add to the count */252 if (changed_vif && !remove_vif)253 data.num_active_macs++;254 255 switch (data.num_active_macs) {256 case 0:257 /* If there are no active macs - change state to SF_INIT_OFF */258 new_state = SF_INIT_OFF;259 break;260 case 1:261 if (remove_vif) {262 /* The one active mac left is of type station263 * and we filled the relevant data during iteration264 */265 new_state = data.sta_vif_state;266 sta = data.sta_vif_ap_sta;267 } else {268 if (WARN_ON(!changed_vif))269 return -EINVAL;270 if (changed_vif->type != NL80211_IFTYPE_STATION) {271 new_state = SF_UNINIT;272 } else if (changed_vif->cfg.assoc &&273 changed_vif->bss_conf.dtim_period) {274 mvmvif = iwl_mvm_vif_from_mac80211(changed_vif);275 sta = mvmvif->ap_sta;276 new_state = SF_FULL_ON;277 } else {278 new_state = SF_INIT_OFF;279 }280 }281 break;282 default:283 /* If there are multiple active macs - change to SF_UNINIT */284 new_state = SF_UNINIT;285 }286 287 return iwl_mvm_sf_config(mvm, sta, new_state);288}289