diff options
Diffstat (limited to 'sys/contrib/dev/iwlwifi/mld/phy.c')
| -rw-r--r-- | sys/contrib/dev/iwlwifi/mld/phy.c | 198 | 
1 files changed, 198 insertions, 0 deletions
| diff --git a/sys/contrib/dev/iwlwifi/mld/phy.c b/sys/contrib/dev/iwlwifi/mld/phy.c new file mode 100644 index 000000000000..1d93fb9e4dbf --- /dev/null +++ b/sys/contrib/dev/iwlwifi/mld/phy.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include <net/mac80211.h> + +#include "phy.h" +#include "hcmd.h" +#include "fw/api/phy-ctxt.h" + +int iwl_mld_allocate_fw_phy_id(struct iwl_mld *mld) +{ +	int id; +	unsigned long used = mld->used_phy_ids; + +	for_each_clear_bit(id, &used, NUM_PHY_CTX) { +		mld->used_phy_ids |= BIT(id); +		return id; +	} + +	return -ENOSPC; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_allocate_fw_phy_id); + +struct iwl_mld_chanctx_usage_data { +	struct iwl_mld *mld; +	struct ieee80211_chanctx_conf *ctx; +	bool use_def; +}; + +static bool iwl_mld_chanctx_fils_enabled(struct ieee80211_vif *vif, +					 struct ieee80211_chanctx_conf *ctx) +{ +	if (vif->type != NL80211_IFTYPE_AP) +		return false; + +	return cfg80211_channel_is_psc(ctx->def.chan) || +		(ctx->def.chan->band == NL80211_BAND_6GHZ && +		 ctx->def.width >= NL80211_CHAN_WIDTH_80); +} + +static void iwl_mld_chanctx_usage_iter(void *_data, u8 *mac, +				       struct ieee80211_vif *vif) +{ +	struct iwl_mld_chanctx_usage_data *data = _data; +	struct ieee80211_bss_conf *link_conf; +	int link_id; + +	for_each_vif_active_link(vif, link_conf, link_id) { +		if (rcu_access_pointer(link_conf->chanctx_conf) != data->ctx) +			continue; + +		if (vif->type == NL80211_IFTYPE_AP && link_conf->ftm_responder) +			data->use_def = true; + +		if (iwl_mld_chanctx_fils_enabled(vif, data->ctx)) +			data->use_def = true; +	} +} + +struct cfg80211_chan_def * +iwl_mld_get_chandef_from_chanctx(struct iwl_mld *mld, +				 struct ieee80211_chanctx_conf *ctx) +{ +	struct iwl_mld_chanctx_usage_data data = { +		.mld = mld, +		.ctx = ctx, +	}; + +	ieee80211_iterate_active_interfaces_mtx(mld->hw, +						IEEE80211_IFACE_ITER_NORMAL, +						iwl_mld_chanctx_usage_iter, +						&data); + +	return data.use_def ? &ctx->def : &ctx->min_def; +} + +static u8 +iwl_mld_nl80211_width_to_fw(enum nl80211_chan_width width) +{ +	switch (width) { +	case NL80211_CHAN_WIDTH_20_NOHT: +	case NL80211_CHAN_WIDTH_20: +		return IWL_PHY_CHANNEL_MODE20; +	case NL80211_CHAN_WIDTH_40: +		return IWL_PHY_CHANNEL_MODE40; +	case NL80211_CHAN_WIDTH_80: +		return IWL_PHY_CHANNEL_MODE80; +	case NL80211_CHAN_WIDTH_160: +		return IWL_PHY_CHANNEL_MODE160; +	case NL80211_CHAN_WIDTH_320: +		return IWL_PHY_CHANNEL_MODE320; +	default: +		WARN(1, "Invalid channel width=%u", width); +		return IWL_PHY_CHANNEL_MODE20; +	} +} + +/* 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) +{ +	int offs = chandef->chan->center_freq - chandef->center_freq1; +	int abs_offs = abs(offs); +	u8 ret; + +	if (offs == 0) { +		/* The FW is expected to check the control channel position only +		 * when in HT/VHT and the channel width is not 20MHz. Return +		 * this value as the default one. +		 */ +		return 0; +	} + +	/* this results in a value 0-7, i.e. fitting into 0b0111 */ +	ret = (abs_offs - 10) / 20; +	/* But we need the value to be in 0b1011 because 0b0100 is +	 * IWL_PHY_CTRL_POS_ABOVE, so shift bit 2 up to land in +	 * IWL_PHY_CTRL_POS_OFFS_EXT (0b1000) +	 */ +	ret = (ret & IWL_PHY_CTRL_POS_OFFS_MSK) | +	      ((ret & BIT(2)) << 1); +	/* and add the above bit */ +	ret |= (offs > 0) * IWL_PHY_CTRL_POS_ABOVE; + +	return ret; +} + +int iwl_mld_phy_fw_action(struct iwl_mld *mld, +			  struct ieee80211_chanctx_conf *ctx, u32 action) +{ +	struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); +	struct cfg80211_chan_def *chandef = &phy->chandef; +	struct iwl_phy_context_cmd cmd = { +		.id_and_color = cpu_to_le32(phy->fw_id), +		.action = cpu_to_le32(action), +		.puncture_mask = cpu_to_le16(chandef->punctured), +		/* Channel info */ +		.ci.channel = cpu_to_le32(chandef->chan->hw_value), +		.ci.band = iwl_mld_nl80211_band_to_fw(chandef->chan->band), +		.ci.width = iwl_mld_nl80211_width_to_fw(chandef->width), +		.ci.ctrl_pos = iwl_mld_get_fw_ctrl_pos(chandef), +	}; +	int ret; + +	if (ctx->ap.chan) { +		cmd.sbb_bandwidth = +			iwl_mld_nl80211_width_to_fw(ctx->ap.width); +		cmd.sbb_ctrl_channel_loc = iwl_mld_get_fw_ctrl_pos(&ctx->ap); +	} + +	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); + +	return ret; +} + +static u32 iwl_mld_get_phy_config(struct iwl_mld *mld) +{ +	u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN | +			   FW_PHY_CFG_RX_CHAIN); +	u32 valid_rx_ant = iwl_mld_get_valid_rx_ant(mld); +	u32 valid_tx_ant = iwl_mld_get_valid_tx_ant(mld); + +	phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS | +		      valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS; + +	return mld->fw->phy_config & phy_config; +} + +int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld) +{ +	const struct iwl_tlv_calib_ctrl *default_calib = +		&mld->fw->default_calib[IWL_UCODE_REGULAR]; +	struct iwl_phy_cfg_cmd_v3 cmd = { +		.phy_cfg = cpu_to_le32(iwl_mld_get_phy_config(mld)), +		.calib_control.event_trigger = default_calib->event_trigger, +		.calib_control.flow_trigger = default_calib->flow_trigger, +		.phy_specific_cfg = mld->fwrt.phy_filters, +	}; + +	IWL_DEBUG_INFO(mld, "Sending Phy CFG command: 0x%x\n", cmd.phy_cfg); + +	return iwl_mld_send_cmd_pdu(mld, PHY_CONFIGURATION_CMD, &cmd); +} + +void iwl_mld_update_phy_chandef(struct iwl_mld *mld, +				struct ieee80211_chanctx_conf *ctx) +{ +	struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); +	struct cfg80211_chan_def *chandef = +		iwl_mld_get_chandef_from_chanctx(mld, ctx); + +	phy->chandef = *chandef; +	iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_MODIFY); +} | 
