Path: blob/main/sys/contrib/dev/iwlwifi/mvm/power.c
48287 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation3* Copyright (C) 2013-2014 Intel Mobile Communications GmbH4* Copyright (C) 2015-2017 Intel Deutschland GmbH5*/6#include <linux/kernel.h>7#include <linux/module.h>8#include <linux/slab.h>9#include <linux/etherdevice.h>1011#include <net/mac80211.h>1213#include "iwl-debug.h"14#include "mvm.h"15#include "iwl-modparams.h"16#include "fw/api/power.h"1718#define POWER_KEEP_ALIVE_PERIOD_SEC 251920static21int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,22struct iwl_beacon_filter_cmd *cmd)23{24u16 len;2526IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",27le32_to_cpu(cmd->ba_enable_beacon_abort));28IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",29le32_to_cpu(cmd->ba_escape_timer));30IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",31le32_to_cpu(cmd->bf_debug_flag));32IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",33le32_to_cpu(cmd->bf_enable_beacon_filter));34IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",35le32_to_cpu(cmd->bf_energy_delta));36IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",37le32_to_cpu(cmd->bf_escape_timer));38IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",39le32_to_cpu(cmd->bf_roaming_energy_delta));40IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",41le32_to_cpu(cmd->bf_roaming_state));42IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",43le32_to_cpu(cmd->bf_temp_threshold));44IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",45le32_to_cpu(cmd->bf_temp_fast_filter));46IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",47le32_to_cpu(cmd->bf_temp_slow_filter));48IWL_DEBUG_POWER(mvm, "bf_threshold_absolute_low is: %d, %d\n",49le32_to_cpu(cmd->bf_threshold_absolute_low[0]),50le32_to_cpu(cmd->bf_threshold_absolute_low[1]));5152IWL_DEBUG_POWER(mvm, "bf_threshold_absolute_high is: %d, %d\n",53le32_to_cpu(cmd->bf_threshold_absolute_high[0]),54le32_to_cpu(cmd->bf_threshold_absolute_high[1]));5556if (fw_has_api(&mvm->fw->ucode_capa,57IWL_UCODE_TLV_API_BEACON_FILTER_V4))58len = sizeof(struct iwl_beacon_filter_cmd);59else60len = offsetof(struct iwl_beacon_filter_cmd,61bf_threshold_absolute_low);6263return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, 0,64len, cmd);65}6667static68void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm,69struct ieee80211_vif *vif,70struct iwl_beacon_filter_cmd *cmd)71{72struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);7374if (vif->bss_conf.cqm_rssi_thold) {75cmd->bf_energy_delta =76cpu_to_le32(vif->bss_conf.cqm_rssi_hyst);77/* fw uses an absolute value for this */78cmd->bf_roaming_state =79cpu_to_le32(-vif->bss_conf.cqm_rssi_thold);80}81cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->ba_enabled);82}8384static void iwl_mvm_power_log(struct iwl_mvm *mvm,85struct iwl_mac_power_cmd *cmd)86{87IWL_DEBUG_POWER(mvm,88"Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",89cmd->id_and_color, iwlmvm_mod_params.power_scheme,90le16_to_cpu(cmd->flags));91IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n",92le16_to_cpu(cmd->keep_alive_seconds));9394if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) {95IWL_DEBUG_POWER(mvm, "Disable power management\n");96return;97}9899IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",100le32_to_cpu(cmd->rx_data_timeout));101IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",102le32_to_cpu(cmd->tx_data_timeout));103if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))104IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",105cmd->skip_dtim_periods);106if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))107IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",108cmd->lprx_rssi_threshold);109if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {110IWL_DEBUG_POWER(mvm, "uAPSD enabled\n");111IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n",112le32_to_cpu(cmd->rx_data_timeout_uapsd));113IWL_DEBUG_POWER(mvm, "Tx timeout (uAPSD) = %u usec\n",114le32_to_cpu(cmd->tx_data_timeout_uapsd));115IWL_DEBUG_POWER(mvm, "QNDP TID = %d\n", cmd->qndp_tid);116IWL_DEBUG_POWER(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags);117IWL_DEBUG_POWER(mvm, "Max SP = %d\n", cmd->uapsd_max_sp);118}119}120121static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,122struct ieee80211_vif *vif,123struct iwl_mac_power_cmd *cmd)124{125struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);126enum ieee80211_ac_numbers ac;127bool tid_found = false;128129if (test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status) ||130cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {131cmd->rx_data_timeout_uapsd =132cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);133cmd->tx_data_timeout_uapsd =134cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);135} else {136cmd->rx_data_timeout_uapsd =137cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);138cmd->tx_data_timeout_uapsd =139cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);140}141142#ifdef CONFIG_IWLWIFI_DEBUGFS143/* set advanced pm flag with no uapsd ACs to enable ps-poll */144if (mvmvif->dbgfs_pm.use_ps_poll) {145cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);146return;147}148#endif149150for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) {151if (!mvmvif->deflink.queue_params[ac].uapsd)152continue;153154if (!test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status))155cmd->flags |=156cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);157158cmd->uapsd_ac_flags |= BIT(ac);159160/* QNDP TID - the highest TID with no admission control */161if (!tid_found && !mvmvif->deflink.queue_params[ac].acm) {162tid_found = true;163switch (ac) {164case IEEE80211_AC_VO:165cmd->qndp_tid = 6;166break;167case IEEE80211_AC_VI:168cmd->qndp_tid = 5;169break;170case IEEE80211_AC_BE:171cmd->qndp_tid = 0;172break;173case IEEE80211_AC_BK:174cmd->qndp_tid = 1;175break;176}177}178}179180cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK);181182if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |183BIT(IEEE80211_AC_VI) |184BIT(IEEE80211_AC_BE) |185BIT(IEEE80211_AC_BK))) {186cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);187cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL);188cmd->snooze_window =189test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status) ?190cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) :191cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW);192}193194cmd->uapsd_max_sp = mvm->hw->uapsd_max_sp_len;195196if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {197cmd->heavy_tx_thld_packets =198IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS;199cmd->heavy_rx_thld_packets =200IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS;201} else {202cmd->heavy_tx_thld_packets =203IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;204cmd->heavy_rx_thld_packets =205IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;206}207cmd->heavy_tx_thld_percentage =208IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;209cmd->heavy_rx_thld_percentage =210IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;211}212213struct iwl_allow_uapsd_iface_iterator_data {214struct ieee80211_vif *current_vif;215bool allow_uapsd;216};217218static void iwl_mvm_allow_uapsd_iterator(void *_data, u8 *mac,219struct ieee80211_vif *vif)220{221struct iwl_allow_uapsd_iface_iterator_data *data = _data;222struct iwl_mvm_vif *other_mvmvif = iwl_mvm_vif_from_mac80211(vif);223struct iwl_mvm_vif *curr_mvmvif =224iwl_mvm_vif_from_mac80211(data->current_vif);225226/* exclude the given vif */227if (vif == data->current_vif)228return;229230switch (vif->type) {231case NL80211_IFTYPE_AP:232case NL80211_IFTYPE_ADHOC:233data->allow_uapsd = false;234break;235case NL80211_IFTYPE_STATION:236/* allow UAPSD if P2P interface and BSS station interface share237* the same channel.238*/239if (vif->cfg.assoc && other_mvmvif->deflink.phy_ctxt &&240curr_mvmvif->deflink.phy_ctxt &&241other_mvmvif->deflink.phy_ctxt->id != curr_mvmvif->deflink.phy_ctxt->id)242data->allow_uapsd = false;243break;244245default:246break;247}248}249250static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,251struct ieee80211_vif *vif)252{253struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);254struct iwl_allow_uapsd_iface_iterator_data data = {255.current_vif = vif,256.allow_uapsd = true,257};258259if (ether_addr_equal(mvmvif->uapsd_misbehaving_ap_addr,260vif->cfg.ap_addr))261return false;262263/*264* Avoid using uAPSD if P2P client is associated to GO that uses265* opportunistic power save. This is due to current FW limitation.266*/267if (vif->p2p &&268(vif->bss_conf.p2p_noa_attr.oppps_ctwindow &269IEEE80211_P2P_OPPPS_ENABLE_BIT))270return false;271272if (vif->p2p && !iwl_mvm_is_p2p_scm_uapsd_supported(mvm))273return false;274275ieee80211_iterate_active_interfaces_atomic(mvm->hw,276IEEE80211_IFACE_ITER_NORMAL,277iwl_mvm_allow_uapsd_iterator,278&data);279280return data.allow_uapsd;281}282283static bool iwl_mvm_power_is_radar(struct ieee80211_bss_conf *link_conf)284{285struct ieee80211_chanctx_conf *chanctx_conf;286287chanctx_conf = rcu_dereference(link_conf->chanctx_conf);288289/* this happens on link switching, just ignore inactive ones */290if (!chanctx_conf)291return false;292293return chanctx_conf->def.chan->flags & IEEE80211_CHAN_RADAR;294}295296static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm,297struct ieee80211_vif *vif,298struct iwl_mac_power_cmd *cmd)299{300struct ieee80211_bss_conf *link_conf;301unsigned int min_link_skip = ~0;302unsigned int link_id;303304/* disable, in case we're supposed to override */305cmd->skip_dtim_periods = 0;306cmd->flags &= ~cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);307308if (!test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status)) {309if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_LP)310return;311cmd->skip_dtim_periods = 2;312cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);313return;314}315316rcu_read_lock();317for_each_vif_active_link(vif, link_conf, link_id) {318unsigned int dtimper = link_conf->dtim_period ?: 1;319unsigned int dtimper_tu = dtimper * link_conf->beacon_int;320unsigned int skip;321322if (dtimper >= 10 || iwl_mvm_power_is_radar(link_conf)) {323rcu_read_unlock();324return;325}326327if (WARN_ON(!dtimper_tu))328continue;329330/* configure skip over dtim up to 900 TU DTIM interval */331skip = max_t(int, 1, 900 / dtimper_tu);332min_link_skip = min(min_link_skip, skip);333}334rcu_read_unlock();335336/* no WARN_ON, can only happen with WARN_ON above */337if (min_link_skip == ~0)338return;339340cmd->skip_dtim_periods = min_link_skip;341cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);342}343344static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,345struct ieee80211_vif *vif,346struct iwl_mac_power_cmd *cmd)347{348int dtimper, bi;349int keep_alive;350struct iwl_mvm_vif *mvmvif __maybe_unused =351iwl_mvm_vif_from_mac80211(vif);352353cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,354mvmvif->color));355dtimper = vif->bss_conf.dtim_period;356bi = vif->bss_conf.beacon_int;357358/*359* Regardless of power management state the driver must set360* keep alive period. FW will use it for sending keep alive NDPs361* immediately after association. Check that keep alive period362* is at least 3 * DTIM363*/364keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),365USEC_PER_SEC);366keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);367cmd->keep_alive_seconds = cpu_to_le16(keep_alive);368369if (mvm->ps_disabled)370return;371372cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);373374if (!vif->cfg.ps || !mvmvif->pm_enabled)375return;376377if (iwl_fw_lookup_cmd_ver(mvm->fw, MAC_PM_POWER_TABLE, 0) >= 2)378cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK);379380if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p &&381(!fw_has_capa(&mvm->fw->ucode_capa,382IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS) ||383!IWL_MVM_P2P_LOWLATENCY_PS_ENABLE))384return;385386cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);387388if (vif->bss_conf.beacon_rate &&389(vif->bss_conf.beacon_rate->bitrate == 10 ||390vif->bss_conf.beacon_rate->bitrate == 60)) {391cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);392cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;393}394395iwl_mvm_power_config_skip_dtim(mvm, vif, cmd);396397if (test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status)) {398cmd->rx_data_timeout =399cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);400cmd->tx_data_timeout =401cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);402} else if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p &&403fw_has_capa(&mvm->fw->ucode_capa,404IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS)) {405cmd->tx_data_timeout =406cpu_to_le32(IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT);407cmd->rx_data_timeout =408cpu_to_le32(IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT);409} else {410cmd->rx_data_timeout =411cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT);412cmd->tx_data_timeout =413cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT);414}415416if (iwl_mvm_power_allow_uapsd(mvm, vif))417iwl_mvm_power_configure_uapsd(mvm, vif, cmd);418419#ifdef CONFIG_IWLWIFI_DEBUGFS420if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)421cmd->keep_alive_seconds =422cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds);423if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {424if (mvmvif->dbgfs_pm.skip_over_dtim)425cmd->flags |=426cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);427else428cmd->flags &=429cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);430}431if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)432cmd->rx_data_timeout =433cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);434if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)435cmd->tx_data_timeout =436cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);437if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)438cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods;439if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {440if (mvmvif->dbgfs_pm.lprx_ena)441cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);442else443cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);444}445if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)446cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold;447if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) {448if (mvmvif->dbgfs_pm.snooze_ena)449cmd->flags |=450cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);451else452cmd->flags &=453cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK);454}455if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) {456u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK;457if (mvmvif->dbgfs_pm.uapsd_misbehaving)458cmd->flags |= cpu_to_le16(flag);459else460cmd->flags &= cpu_to_le16(flag);461}462#endif /* CONFIG_IWLWIFI_DEBUGFS */463}464465static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,466struct ieee80211_vif *vif)467{468struct iwl_mac_power_cmd cmd = {};469470iwl_mvm_power_build_cmd(mvm, vif, &cmd);471iwl_mvm_power_log(mvm, &cmd);472#ifdef CONFIG_IWLWIFI_DEBUGFS473memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd));474#endif475476return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, 0,477sizeof(cmd), &cmd);478}479480int iwl_mvm_power_update_device(struct iwl_mvm *mvm)481{482struct iwl_device_power_cmd cmd = {483.flags = 0,484};485486if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)487mvm->ps_disabled = true;488489if (!mvm->ps_disabled)490cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);491492#ifdef CONFIG_IWLWIFI_DEBUGFS493if (test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status) ?494mvm->disable_power_off_d3 : mvm->disable_power_off)495cmd.flags &=496cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);497#endif498if (mvm->ext_clock_valid)499cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_32K_CLK_VALID_MSK);500501if (iwl_fw_lookup_cmd_ver(mvm->fw, POWER_TABLE_CMD, 0) >= 7 &&502test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status))503cmd.flags |=504cpu_to_le16(DEVICE_POWER_FLAGS_NO_SLEEP_TILL_D3_MSK);505506IWL_DEBUG_POWER(mvm,507"Sending device power command with flags = 0x%X\n",508cmd.flags);509510return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, 0, sizeof(cmd),511&cmd);512}513514void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)515{516struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);517518if (!ether_addr_equal(mvmvif->uapsd_misbehaving_ap_addr,519vif->cfg.ap_addr))520eth_zero_addr(mvmvif->uapsd_misbehaving_ap_addr);521}522523static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac,524struct ieee80211_vif *vif)525{526u8 *ap_sta_id = _data;527struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);528struct ieee80211_bss_conf *link_conf;529unsigned int link_id;530531rcu_read_lock();532for_each_vif_active_link(vif, link_conf, link_id) {533struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];534535/* The ap_sta_id is not expected to change during current536* association so no explicit protection is needed537*/538if (link_info->ap_sta_id == *ap_sta_id) {539ether_addr_copy(mvmvif->uapsd_misbehaving_ap_addr,540vif->cfg.ap_addr);541break;542}543}544rcu_read_unlock();545}546547void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,548struct iwl_rx_cmd_buffer *rxb)549{550struct iwl_rx_packet *pkt = rxb_addr(rxb);551struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data;552u8 ap_sta_id = le32_to_cpu(notif->sta_id);553554ieee80211_iterate_active_interfaces_atomic(555mvm->hw, IEEE80211_IFACE_ITER_NORMAL,556iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id);557}558559struct iwl_power_vifs {560struct iwl_mvm *mvm;561struct ieee80211_vif *bss_vif;562struct ieee80211_vif *p2p_vif;563struct ieee80211_vif *ap_vif;564struct ieee80211_vif *monitor_vif;565bool p2p_active;566bool bss_active;567bool ap_active;568bool monitor_active;569};570571static void iwl_mvm_power_disable_pm_iterator(void *_data, u8 *mac,572struct ieee80211_vif *vif)573{574struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);575576mvmvif->pm_enabled = false;577}578579static void iwl_mvm_power_ps_disabled_iterator(void *_data, u8 *mac,580struct ieee80211_vif *vif)581{582struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);583bool *disable_ps = _data;584585if (iwl_mvm_vif_is_active(mvmvif))586*disable_ps |= mvmvif->ps_disabled;587}588589static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac,590struct ieee80211_vif *vif)591{592struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);593struct iwl_power_vifs *power_iterator = _data;594bool active;595596if (!mvmvif->uploaded)597return;598599active = iwl_mvm_vif_is_active(mvmvif);600601switch (ieee80211_vif_type_p2p(vif)) {602case NL80211_IFTYPE_P2P_DEVICE:603break;604605case NL80211_IFTYPE_P2P_GO:606case NL80211_IFTYPE_AP:607/* only a single MAC of the same type */608WARN_ON(power_iterator->ap_vif);609power_iterator->ap_vif = vif;610if (active)611power_iterator->ap_active = true;612break;613614case NL80211_IFTYPE_MONITOR:615/* only a single MAC of the same type */616WARN_ON(power_iterator->monitor_vif);617power_iterator->monitor_vif = vif;618if (active)619power_iterator->monitor_active = true;620break;621622case NL80211_IFTYPE_P2P_CLIENT:623/* only a single MAC of the same type */624WARN_ON(power_iterator->p2p_vif);625power_iterator->p2p_vif = vif;626if (active)627power_iterator->p2p_active = true;628break;629630case NL80211_IFTYPE_STATION:631power_iterator->bss_vif = vif;632if (active)633power_iterator->bss_active = true;634break;635636default:637break;638}639}640641static void iwl_mvm_power_set_pm(struct iwl_mvm *mvm,642struct iwl_power_vifs *vifs)643{644struct iwl_mvm_vif *bss_mvmvif = NULL;645struct iwl_mvm_vif *p2p_mvmvif = NULL;646struct iwl_mvm_vif *ap_mvmvif = NULL;647bool client_same_channel = false;648bool ap_same_channel = false;649650lockdep_assert_held(&mvm->mutex);651652/* set pm_enable to false */653ieee80211_iterate_active_interfaces_atomic(mvm->hw,654IEEE80211_IFACE_ITER_NORMAL,655iwl_mvm_power_disable_pm_iterator,656NULL);657658if (vifs->bss_vif)659bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif);660661if (vifs->p2p_vif)662p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif);663664if (vifs->ap_vif)665ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif);666667/* don't allow PM if any TDLS stations exist */668if (iwl_mvm_tdls_sta_count(mvm, NULL))669return;670671/* enable PM on bss if bss stand alone */672if (bss_mvmvif && vifs->bss_active && !vifs->p2p_active &&673!vifs->ap_active) {674bss_mvmvif->pm_enabled = true;675return;676}677678/* enable PM on p2p if p2p stand alone */679if (p2p_mvmvif && vifs->p2p_active && !vifs->bss_active &&680!vifs->ap_active) {681p2p_mvmvif->pm_enabled = true;682return;683}684685if (p2p_mvmvif && bss_mvmvif && vifs->bss_active && vifs->p2p_active)686client_same_channel =687iwl_mvm_have_links_same_channel(bss_mvmvif, p2p_mvmvif);688689if (bss_mvmvif && ap_mvmvif && vifs->bss_active && vifs->ap_active)690ap_same_channel =691iwl_mvm_have_links_same_channel(bss_mvmvif, ap_mvmvif);692693/* clients are not stand alone: enable PM if DCM */694if (!(client_same_channel || ap_same_channel)) {695if (bss_mvmvif && vifs->bss_active)696bss_mvmvif->pm_enabled = true;697if (p2p_mvmvif && vifs->p2p_active)698p2p_mvmvif->pm_enabled = true;699return;700}701702/*703* There is only one channel in the system and there are only704* bss and p2p clients that share it705*/706if (client_same_channel && !vifs->ap_active) {707/* share same channel*/708bss_mvmvif->pm_enabled = true;709p2p_mvmvif->pm_enabled = true;710}711}712713#ifdef CONFIG_IWLWIFI_DEBUGFS714int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,715struct ieee80211_vif *vif, char *buf,716int bufsz)717{718struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);719struct iwl_mac_power_cmd cmd = {};720int pos = 0;721722mutex_lock(&mvm->mutex);723memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd));724mutex_unlock(&mvm->mutex);725726pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",727iwlmvm_mod_params.power_scheme);728pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",729le16_to_cpu(cmd.flags));730pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",731le16_to_cpu(cmd.keep_alive_seconds));732733if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)))734return pos;735736pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",737(cmd.flags &738cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0);739pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",740cmd.skip_dtim_periods);741if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {742pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",743le32_to_cpu(cmd.rx_data_timeout));744pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",745le32_to_cpu(cmd.tx_data_timeout));746}747if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))748pos += scnprintf(buf+pos, bufsz-pos,749"lprx_rssi_threshold = %d\n",750cmd.lprx_rssi_threshold);751752if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)))753return pos;754755pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n",756le32_to_cpu(cmd.rx_data_timeout_uapsd));757pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n",758le32_to_cpu(cmd.tx_data_timeout_uapsd));759pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid);760pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n",761cmd.uapsd_ac_flags);762pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n",763cmd.uapsd_max_sp);764pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n",765cmd.heavy_tx_thld_packets);766pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n",767cmd.heavy_rx_thld_packets);768pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n",769cmd.heavy_tx_thld_percentage);770pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n",771cmd.heavy_rx_thld_percentage);772pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n",773(cmd.flags &774cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ?7751 : 0);776777if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)))778return pos;779780pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n",781cmd.snooze_interval);782pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n",783cmd.snooze_window);784785return pos;786}787788void789iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,790struct iwl_beacon_filter_cmd *cmd)791{792struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);793struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;794795if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA)796cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta);797if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA)798cmd->bf_roaming_energy_delta =799cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta);800if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE)801cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state);802if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD)803cmd->bf_temp_threshold =804cpu_to_le32(dbgfs_bf->bf_temp_threshold);805if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER)806cmd->bf_temp_fast_filter =807cpu_to_le32(dbgfs_bf->bf_temp_fast_filter);808if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER)809cmd->bf_temp_slow_filter =810cpu_to_le32(dbgfs_bf->bf_temp_slow_filter);811if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG)812cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag);813if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER)814cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer);815if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER)816cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer);817if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT)818cmd->ba_enable_beacon_abort =819cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort);820}821#endif822823static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,824struct ieee80211_vif *vif,825struct iwl_beacon_filter_cmd *cmd)826{827struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);828int ret;829830if (mvmvif != mvm->bf_allowed_vif || !vif->bss_conf.dtim_period ||831vif->type != NL80211_IFTYPE_STATION || vif->p2p)832return 0;833834iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd);835iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd);836ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd);837838if (!ret)839mvmvif->bf_enabled = true;840841return ret;842}843844int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,845struct ieee80211_vif *vif)846{847struct iwl_beacon_filter_cmd cmd = {848IWL_BF_CMD_CONFIG_DEFAULTS,849.bf_enable_beacon_filter = cpu_to_le32(1),850};851852return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd);853}854855static int _iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,856struct ieee80211_vif *vif)857{858struct iwl_beacon_filter_cmd cmd = {};859struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);860int ret;861862if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)863return 0;864865ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);866867if (!ret)868mvmvif->bf_enabled = false;869870return ret;871}872873int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,874struct ieee80211_vif *vif)875{876return _iwl_mvm_disable_beacon_filter(mvm, vif);877}878879static int iwl_mvm_power_set_ps(struct iwl_mvm *mvm)880{881bool disable_ps;882int ret;883884/* disable PS if CAM */885disable_ps = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM);886/* ...or if any of the vifs require PS to be off */887ieee80211_iterate_active_interfaces_atomic(mvm->hw,888IEEE80211_IFACE_ITER_NORMAL,889iwl_mvm_power_ps_disabled_iterator,890&disable_ps);891892/* update device power state if it has changed */893if (mvm->ps_disabled != disable_ps) {894bool old_ps_disabled = mvm->ps_disabled;895896mvm->ps_disabled = disable_ps;897ret = iwl_mvm_power_update_device(mvm);898if (ret) {899mvm->ps_disabled = old_ps_disabled;900return ret;901}902}903904return 0;905}906907static int iwl_mvm_power_set_ba(struct iwl_mvm *mvm,908struct ieee80211_vif *vif)909{910struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);911struct iwl_beacon_filter_cmd cmd = {912IWL_BF_CMD_CONFIG_DEFAULTS,913.bf_enable_beacon_filter = cpu_to_le32(1),914};915916if (!mvmvif->bf_enabled)917return 0;918919if (test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status))920cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);921922mvmvif->ba_enabled = !(!mvmvif->pm_enabled ||923mvm->ps_disabled ||924!vif->cfg.ps ||925iwl_mvm_vif_low_latency(mvmvif));926927return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd);928}929930int iwl_mvm_power_update_ps(struct iwl_mvm *mvm)931{932struct iwl_power_vifs vifs = {933.mvm = mvm,934};935int ret;936937lockdep_assert_held(&mvm->mutex);938939/* get vifs info */940ieee80211_iterate_active_interfaces_atomic(mvm->hw,941IEEE80211_IFACE_ITER_NORMAL,942iwl_mvm_power_get_vifs_iterator, &vifs);943944ret = iwl_mvm_power_set_ps(mvm);945if (ret)946return ret;947948if (vifs.bss_vif)949return iwl_mvm_power_set_ba(mvm, vifs.bss_vif);950951return 0;952}953954int iwl_mvm_power_update_mac(struct iwl_mvm *mvm)955{956struct iwl_power_vifs vifs = {957.mvm = mvm,958};959int ret;960961lockdep_assert_held(&mvm->mutex);962963/* get vifs info */964ieee80211_iterate_active_interfaces_atomic(mvm->hw,965IEEE80211_IFACE_ITER_NORMAL,966iwl_mvm_power_get_vifs_iterator, &vifs);967968iwl_mvm_power_set_pm(mvm, &vifs);969970ret = iwl_mvm_power_set_ps(mvm);971if (ret)972return ret;973974if (vifs.bss_vif) {975ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif);976if (ret)977return ret;978}979980if (vifs.p2p_vif) {981ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif);982if (ret)983return ret;984}985986if (vifs.bss_vif)987return iwl_mvm_power_set_ba(mvm, vifs.bss_vif);988989return 0;990}991992993