// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2022-2024 Intel Corporation
*/
#include "mvm.h"
#include "time-sync.h"
#include "sta.h"
u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
int filter_link_id)
{
struct ieee80211_link_sta *link_sta;
struct iwl_mvm_sta *mvmsta;
struct ieee80211_vif *vif;
unsigned int link_id;
u32 result = 0;
if (!sta)
return 0;
mvmsta = iwl_mvm_sta_from_mac80211(sta);
vif = mvmsta->vif;
/* it's easy when the STA is not an MLD */
if (!sta->valid_links)
return BIT(mvmsta->deflink.sta_id);
/* but if it is an MLD, get the mask of all the FW STAs it has ... */
for_each_sta_active_link(vif, sta, link_sta, link_id) {
struct iwl_mvm_link_sta *mvm_link_sta;
/* unless we have a specific link in mind */
if (filter_link_id >= 0 && link_id != filter_link_id)
continue;
mvm_link_sta =
rcu_dereference_check(mvmsta->link[link_id],
lockdep_is_held(&mvm->mutex));
if (!mvm_link_sta)
continue;
result |= BIT(mvm_link_sta->sta_id);
}
return result;
}
static int iwl_mvm_mld_send_sta_cmd(struct iwl_mvm *mvm,
struct iwl_sta_cfg_cmd *cmd)
{
int ret = iwl_mvm_send_cmd_pdu(mvm,
WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD),
0, sizeof(*cmd), cmd);
if (ret)
IWL_ERR(mvm, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret);
return ret;
}
/*
* Add an internal station to the FW table
*/
static int iwl_mvm_mld_add_int_sta_to_fw(struct iwl_mvm *mvm,
struct iwl_mvm_int_sta *sta,
const u8 *addr, int link_id)
{
struct iwl_sta_cfg_cmd cmd;
lockdep_assert_held(&mvm->mutex);
memset(&cmd, 0, sizeof(cmd));
cmd.sta_id = cpu_to_le32((u8)sta->sta_id);
cmd.link_id = cpu_to_le32(link_id);
cmd.station_type = cpu_to_le32(sta->type);
if (fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_STA_EXP_MFP_SUPPORT) &&
sta->type == STATION_TYPE_BCAST_MGMT)
cmd.mfp = cpu_to_le32(1);
if (addr) {
memcpy(cmd.peer_mld_address, addr, ETH_ALEN);
memcpy(cmd.peer_link_address, addr, ETH_ALEN);
}
return iwl_mvm_mld_send_sta_cmd(mvm, &cmd);
}
/*
* Remove a station from the FW table. Before sending the command to remove
* the station validate that the station is indeed known to the driver (sanity
* only).
*/
static int iwl_mvm_mld_rm_sta_from_fw(struct iwl_mvm *mvm, u32 sta_id)
{
struct iwl_remove_sta_cmd rm_sta_cmd = {
.sta_id = cpu_to_le32(sta_id),
};
int ret;
/* Note: internal stations are marked as error values */
if (!rcu_access_pointer(mvm->fw_id_to_mac_id[sta_id])) {
IWL_ERR(mvm, "Invalid station id %d\n", sta_id);
return -EINVAL;
}
ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, STA_REMOVE_CMD),
0, sizeof(rm_sta_cmd), &rm_sta_cmd);
if (ret) {
IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id);
return ret;
}
return 0;
}
static int iwl_mvm_add_aux_sta_to_fw(struct iwl_mvm *mvm,
struct iwl_mvm_int_sta *sta,
u32 lmac_id)
{
int ret;
struct iwl_mvm_aux_sta_cmd cmd = {
.sta_id = <