Path: blob/main/sys/contrib/dev/iwlwifi/mld/phy.c
107228 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2024-2025 Intel Corporation3*/4#include <net/mac80211.h>56#include "phy.h"7#include "hcmd.h"8#include "fw/api/phy-ctxt.h"910int iwl_mld_allocate_fw_phy_id(struct iwl_mld *mld)11{12int id;13unsigned long used = mld->used_phy_ids;1415for_each_clear_bit(id, &used, NUM_PHY_CTX) {16mld->used_phy_ids |= BIT(id);17return id;18}1920return -ENOSPC;21}22EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_allocate_fw_phy_id);2324struct iwl_mld_chanctx_usage_data {25struct iwl_mld *mld;26struct ieee80211_chanctx_conf *ctx;27bool use_def;28};2930static bool iwl_mld_chanctx_fils_enabled(struct ieee80211_vif *vif,31struct ieee80211_chanctx_conf *ctx)32{33if (vif->type != NL80211_IFTYPE_AP)34return false;3536return cfg80211_channel_is_psc(ctx->def.chan) ||37(ctx->def.chan->band == NL80211_BAND_6GHZ &&38ctx->def.width >= NL80211_CHAN_WIDTH_80);39}4041static void iwl_mld_chanctx_usage_iter(void *_data, u8 *mac,42struct ieee80211_vif *vif)43{44struct iwl_mld_chanctx_usage_data *data = _data;45struct ieee80211_bss_conf *link_conf;46int link_id;4748for_each_vif_active_link(vif, link_conf, link_id) {49if (rcu_access_pointer(link_conf->chanctx_conf) != data->ctx)50continue;5152if (vif->type == NL80211_IFTYPE_AP && link_conf->ftm_responder)53data->use_def = true;5455if (iwl_mld_chanctx_fils_enabled(vif, data->ctx))56data->use_def = true;57}58}5960struct cfg80211_chan_def *61iwl_mld_get_chandef_from_chanctx(struct iwl_mld *mld,62struct ieee80211_chanctx_conf *ctx)63{64struct iwl_mld_chanctx_usage_data data = {65.mld = mld,66.ctx = ctx,67};6869ieee80211_iterate_active_interfaces_mtx(mld->hw,70IEEE80211_IFACE_ITER_NORMAL,71iwl_mld_chanctx_usage_iter,72&data);7374return data.use_def ? &ctx->def : &ctx->min_def;75}7677static u878iwl_mld_nl80211_width_to_fw(enum nl80211_chan_width width)79{80switch (width) {81case NL80211_CHAN_WIDTH_20_NOHT:82case NL80211_CHAN_WIDTH_20:83return IWL_PHY_CHANNEL_MODE20;84case NL80211_CHAN_WIDTH_40:85return IWL_PHY_CHANNEL_MODE40;86case NL80211_CHAN_WIDTH_80:87return IWL_PHY_CHANNEL_MODE80;88case NL80211_CHAN_WIDTH_160:89return IWL_PHY_CHANNEL_MODE160;90case NL80211_CHAN_WIDTH_320:91return IWL_PHY_CHANNEL_MODE320;92default:93WARN(1, "Invalid channel width=%u", width);94return IWL_PHY_CHANNEL_MODE20;95}96}9798/* Maps the driver specific control channel position (relative to the center99* freq) definitions to the fw values100*/101u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef)102{103int offs = chandef->chan->center_freq - chandef->center_freq1;104int abs_offs = abs(offs);105u8 ret;106107if (offs == 0) {108/* The FW is expected to check the control channel position only109* when in HT/VHT and the channel width is not 20MHz. Return110* this value as the default one.111*/112return 0;113}114115/* this results in a value 0-7, i.e. fitting into 0b0111 */116ret = (abs_offs - 10) / 20;117/* But we need the value to be in 0b1011 because 0b0100 is118* IWL_PHY_CTRL_POS_ABOVE, so shift bit 2 up to land in119* IWL_PHY_CTRL_POS_OFFS_EXT (0b1000)120*/121ret = (ret & IWL_PHY_CTRL_POS_OFFS_MSK) |122((ret & BIT(2)) << 1);123/* and add the above bit */124ret |= (offs > 0) * IWL_PHY_CTRL_POS_ABOVE;125126return ret;127}128129int iwl_mld_phy_fw_action(struct iwl_mld *mld,130struct ieee80211_chanctx_conf *ctx, u32 action)131{132struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);133struct cfg80211_chan_def *chandef = &phy->chandef;134struct iwl_phy_context_cmd cmd = {135.id_and_color = cpu_to_le32(phy->fw_id),136.action = cpu_to_le32(action),137.puncture_mask = cpu_to_le16(chandef->punctured),138/* Channel info */139.ci.channel = cpu_to_le32(chandef->chan->hw_value),140.ci.band = iwl_mld_nl80211_band_to_fw(chandef->chan->band),141.ci.width = iwl_mld_nl80211_width_to_fw(chandef->width),142.ci.ctrl_pos = iwl_mld_get_fw_ctrl_pos(chandef),143};144int ret;145146if (ctx->ap.chan) {147cmd.sbb_bandwidth =148iwl_mld_nl80211_width_to_fw(ctx->ap.width);149cmd.sbb_ctrl_channel_loc = iwl_mld_get_fw_ctrl_pos(&ctx->ap);150}151152ret = iwl_mld_send_cmd_pdu(mld, PHY_CONTEXT_CMD, &cmd);153if (ret)154IWL_ERR(mld, "Failed to send PHY_CONTEXT_CMD ret = %d\n", ret);155156return ret;157}158159static u32 iwl_mld_get_phy_config(struct iwl_mld *mld)160{161u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN |162FW_PHY_CFG_RX_CHAIN);163u32 valid_rx_ant = iwl_mld_get_valid_rx_ant(mld);164u32 valid_tx_ant = iwl_mld_get_valid_tx_ant(mld);165166phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS |167valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS;168169return mld->fw->phy_config & phy_config;170}171172int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld)173{174const struct iwl_tlv_calib_ctrl *default_calib =175&mld->fw->default_calib[IWL_UCODE_REGULAR];176struct iwl_phy_cfg_cmd_v3 cmd = {177.phy_cfg = cpu_to_le32(iwl_mld_get_phy_config(mld)),178.calib_control.event_trigger = default_calib->event_trigger,179.calib_control.flow_trigger = default_calib->flow_trigger,180.phy_specific_cfg = mld->fwrt.phy_filters,181};182183IWL_DEBUG_INFO(mld, "Sending Phy CFG command: 0x%x\n", cmd.phy_cfg);184185return iwl_mld_send_cmd_pdu(mld, PHY_CONFIGURATION_CMD, &cmd);186}187188void iwl_mld_update_phy_chandef(struct iwl_mld *mld,189struct ieee80211_chanctx_conf *ctx)190{191struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx);192struct cfg80211_chan_def *chandef =193iwl_mld_get_chandef_from_chanctx(mld, ctx);194195phy->chandef = *chandef;196iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_MODIFY);197}198199200