// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2024-2025 Intel Corporation
*/
#include "constants.h"
#include "link.h"
#include "iface.h"
#include "mlo.h"
#include "hcmd.h"
#include "phy.h"
#include "fw/api/rs.h"
#include "fw/api/txq.h"
#include "fw/api/mac.h"
#include "fw/api/context.h"
#include "fw/dbg.h"
static int iwl_mld_send_link_cmd(struct iwl_mld *mld,
struct iwl_link_config_cmd *cmd,
enum iwl_ctxt_action action)
{
int ret;
lockdep_assert_wiphy(mld->wiphy);
cmd->action = cpu_to_le32(action);
ret = iwl_mld_send_cmd_pdu(mld,
WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD),
cmd);
if (ret)
IWL_ERR(mld, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n",
action, ret);
return ret;
}
static int iwl_mld_add_link_to_fw(struct iwl_mld *mld,
struct ieee80211_bss_conf *link_conf)
{
struct ieee80211_vif *vif = link_conf->vif;
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
struct iwl_mld_link *link = iwl_mld_link_from_mac80211(link_conf);
struct iwl_link_config_cmd cmd = {};
lockdep_assert_wiphy(mld->wiphy);
if (WARN_ON(!link))
return -EINVAL;
cmd.link_id = cpu_to_le32(link->fw_id);
cmd.mac_id = cpu_to_le32(mld_vif->fw_id);
cmd.spec_link_id = link_conf->link_id;
cmd.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID);
ether_addr_copy(cmd.local_link_addr, link_conf->addr);
if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid)
ether_addr_copy(cmd.ibss_bssid_addr, link_conf->bssid);
return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_ADD);
}
/* Get the basic rates of the used band and add the mandatory ones */
static void iwl_mld_fill_rates(struct iwl_mld *mld,
struct ieee80211_bss_conf *link,
struct ieee80211_chanctx_conf *chan_ctx,
__le32 *cck_rates, __le32 *ofdm_rates)
{
struct cfg80211_chan_def *chandef =
iwl_mld_get_chandef_from_chanctx(mld, chan_ctx);
struct ieee80211_supported_band *sband =
mld->hw->wiphy->bands[chandef->chan->band];
unsigned long basic = link->basic_rates;
int lowest_present_ofdm = 100;
int lowest_present_cck = 100;
u32 cck = 0;
u32 ofdm = 0;
int i;
for_each_set_bit(i, &basic, BITS_PER_LONG) {
int hw = sband->bitrates[i].hw_value;
if (hw >= IWL_FIRST_OFDM_RATE) {
ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE);
if (lowest_present_ofdm > hw)
lowest_present_ofdm = hw;
} else {
BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
cck |= BIT(hw);
if (lowest_present_cck > hw)
lowest_present_cck = hw;
}
}
/* Now we've got the basic rates as bitmaps in the ofdm and cck
* variables. This isn't sufficient though, as there might not
* be all the right rates in the bitmap. E.g. if the only basic
* rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps
* and 6 Mbps because the 802.11-2007 standard says in 9.6:
*
* [...] a STA responding to a received frame shall transmit
* its Control Response frame [...] at the highest rate in the
* BSSBasicRateSet parameter that is less than or equal to the
* rate of the immediately previous frame in the frame exchange
* sequence ([...]) and that is of the same modulation class
* ([...]) as the received frame. If no rate contained in the
* BSSBasicRateSet parameter meets these conditions, then the
* control frame sent in response to a received frame shall be
* transmitted at the highest mandatory rate of the PHY that is
* less than or equal to the rate of the received frame, and
* that is of the same modulation class as the received frame.
*
* As a consequence, we need to add all mandatory rates that are
* lower than all of the basic rates to these bitmaps.
*/
if (lowest_present_ofdm > IWL_RATE_24M_INDEX)
ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE;
if (lowest_present_ofdm > IWL_RATE_12M_INDEX)
ofdm |= IWL_RATE_BIT_MSK(12) >>