* 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 */
/**
* @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
*/
.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,
+ },
},
},
{
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);
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)
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 &&
link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS;
}
+ if (changes & BSS_CHANGED_NPCA)
+ link_changes |= LINK_CONTEXT_MODIFY_UHR_PARAMS;
+
return link_changes;
}
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#include <net/mac80211.h>
/* 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;
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)
{
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);