From: Johannes Berg UHR devices have NPCA capability, so implement the necessary configuration and set the capability bit. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit --- .../wireless/intel/iwlwifi/fw/api/mac-cfg.h | 4 +- .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 3 ++ drivers/net/wireless/intel/iwlwifi/mld/link.c | 15 +++++++ .../net/wireless/intel/iwlwifi/mld/mac80211.c | 39 ++++++++++++++++++- drivers/net/wireless/intel/iwlwifi/mld/phy.c | 24 ++++++++++-- 5 files changed, 80 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index dde6cfd9d286..fa2fc6a92e60 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -549,12 +549,14 @@ enum iwl_link_ctx_protection_flags { * radar pulses). * @LINK_FLG_NDP_FEEDBACK_ENABLED: mark support for NDP feedback and change * of threshold + * @LINK_FLG_NPCA: NPCA enabled */ enum iwl_link_ctx_flags { LINK_FLG_BSS_COLOR_DIS = BIT(0), LINK_FLG_MU_EDCA_CW = BIT(1), LINK_FLG_RU_2MHZ_BLOCK = BIT(2), LINK_FLG_NDP_FEEDBACK_ENABLED = BIT(3), + LINK_FLG_NPCA = BIT(4), }; /* LINK_CONTEXT_FLAG_E_VER_1 */ /** @@ -591,7 +593,7 @@ enum iwl_npca_flags { * @initial_qsrc: Indicates the value that is used to initialize the * EDCAF QSRC[AC] variables * @min_dur_threshold: minimum PPDU time to switch to the non-primary - * NPCA channel (usec) + * NPCA channel (spec representation) * @flags: NPCA flags, see &enum iwl_npca_flags * @reserved: reserved for alignment purposes */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 38ff6f944add..4e8222ffe732 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -695,6 +695,9 @@ static const struct ieee80211_sband_iftype_data iwl_iftype_cap[] = { .has_uhr = true, .phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX | IEEE80211_UHR_PHY_CAP_ELR_TX, + .mac.mac_cap = { + [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP, + }, }, }, { diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index 805f2e2eac38..9ef39b03e67f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -374,6 +374,21 @@ iwl_mld_change_link_in_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link, if (WARN_ON(changes & LINK_CONTEXT_MODIFY_EHT_PARAMS)) changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS; + if (link->uhr_support && link->npca.enabled) { + flags |= LINK_FLG_NPCA; + if (link->npca.moplen) + cmd.npca_params.flags |= IWL_NPCA_FLAG_MAC_HDR_BASED; + cmd.npca_params.dis_subch_bmap = + cpu_to_le16(link->chanreq.oper.npca_punctured); + cmd.npca_params.initial_qsrc = link->npca.init_qsrc; + cmd.npca_params.min_dur_threshold = link->npca.min_dur_thresh; + /* spec/mac80211 have these in units of 4 usec */ + cmd.npca_params.switch_delay = + 4 * link->npca.switch_delay; + cmd.npca_params.switch_back_delay = + 4 * link->npca.switch_back_delay; + } + send_cmd: cmd.modify_mask = cpu_to_le32(changes); cmd.flags = cpu_to_le32(flags); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 41bc47e4e00c..1f2843af39c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -981,6 +981,30 @@ void iwl_mld_remove_chanctx(struct ieee80211_hw *hw, mld->used_phy_ids &= ~BIT(phy->fw_id); } +static void +iwl_mld_update_link_npca_puncturing(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct ieee80211_vif *vif; + + for_each_active_interface(vif, hw) { + struct ieee80211_bss_conf *link; + int link_id; + + for_each_vif_active_link(vif, link, link_id) { + if (rcu_access_pointer(link->chanctx_conf) != ctx) + continue; + + if (!link->npca.enabled) + continue; + + iwl_mld_change_link_in_fw(mld, link, + LINK_CONTEXT_MODIFY_UHR_PARAMS); + } + } +} + static void iwl_mld_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx, u32 changed) @@ -996,9 +1020,19 @@ void iwl_mld_change_chanctx(struct ieee80211_hw *hw, IEEE80211_CHANCTX_CHANGE_CHANNEL))) return; + /* NPCA puncturing is in link API for FW */ + if (changed & IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT) { + iwl_mld_update_link_npca_puncturing(hw, ctx); + changed &= ~IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT; + } + /* Check if a FW update is required */ - if (changed & IEEE80211_CHANCTX_CHANGE_AP) + if (!changed) + return; + + if (changed & IEEE80211_CHANCTX_CHANGE_AP || + changed & IEEE80211_CHANCTX_CHANGE_NPCA) goto update; if (chandef->chan == phy->chandef.chan && @@ -1255,6 +1289,9 @@ u32 iwl_mld_link_changed_mapping(struct iwl_mld *mld, link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; } + if (changes & BSS_CHANGED_NPCA) + link_changes |= LINK_CONTEXT_MODIFY_UHR_PARAMS; + return link_changes; } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.c b/drivers/net/wireless/intel/iwlwifi/mld/phy.c index 1d93fb9e4dbf..59bf088ead84 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/phy.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -99,9 +99,9 @@ iwl_mld_nl80211_width_to_fw(enum nl80211_chan_width width) /* Maps the driver specific control channel position (relative to the center * freq) definitions to the fw values */ -u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef) +static u8 _iwl_mld_get_fw_ctrl_pos(u32 control, u32 cf1) { - int offs = chandef->chan->center_freq - chandef->center_freq1; + int offs = control - cf1; int abs_offs = abs(offs); u8 ret; @@ -127,6 +127,12 @@ u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef) return ret; } +u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef) +{ + return _iwl_mld_get_fw_ctrl_pos(chandef->chan->center_freq, + chandef->center_freq1); +} + int iwl_mld_phy_fw_action(struct iwl_mld *mld, struct ieee80211_chanctx_conf *ctx, u32 action) { @@ -150,6 +156,18 @@ int iwl_mld_phy_fw_action(struct iwl_mld *mld, cmd.sbb_ctrl_channel_loc = iwl_mld_get_fw_ctrl_pos(&ctx->ap); } + /* + * Set NPCA channel if NPCA is used; if not used, just set it to an + * arbitrary channel on the other side to help firmware. + */ + if (chandef->npca_chan) + cmd.secondary_ctrl_chnl_loc = + _iwl_mld_get_fw_ctrl_pos(chandef->npca_chan->center_freq, + chandef->center_freq1); + else + cmd.secondary_ctrl_chnl_loc = + cmd.ci.ctrl_pos ^ IWL_PHY_CTRL_POS_ABOVE; + ret = iwl_mld_send_cmd_pdu(mld, PHY_CONTEXT_CMD, &cmd); if (ret) IWL_ERR(mld, "Failed to send PHY_CONTEXT_CMD ret = %d\n", ret); -- 2.34.1