Path: blob/main/sys/contrib/dev/iwlwifi/mld/mcc.c
107001 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2024-2025 Intel Corporation3*/45#include <net/cfg80211.h>6#include <net/mac80211.h>78#include <fw/dbg.h>9#include <iwl-nvm-parse.h>1011#include "mld.h"12#include "hcmd.h"13#include "mcc.h"1415/* It is the caller's responsibility to free the pointer returned here */16static struct iwl_mcc_update_resp_v8 *17iwl_mld_copy_mcc_resp(const struct iwl_rx_packet *pkt)18{19const struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (const void *)pkt->data;20int n_channels = __le32_to_cpu(mcc_resp_v8->n_channels);21struct iwl_mcc_update_resp_v8 *resp_cp;22int notif_len = struct_size(resp_cp, channels, n_channels);2324if (iwl_rx_packet_payload_len(pkt) != notif_len)25return ERR_PTR(-EINVAL);2627resp_cp = kmemdup(mcc_resp_v8, notif_len, GFP_KERNEL);28if (!resp_cp)29return ERR_PTR(-ENOMEM);3031return resp_cp;32}3334/* It is the caller's responsibility to free the pointer returned here */35static struct iwl_mcc_update_resp_v8 *36iwl_mld_update_mcc(struct iwl_mld *mld, const char *alpha2,37enum iwl_mcc_source src_id)38{39struct iwl_mcc_update_cmd mcc_update_cmd = {40.mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]),41.source_id = (u8)src_id,42};43struct iwl_mcc_update_resp_v8 *resp_cp;44struct iwl_rx_packet *pkt;45struct iwl_host_cmd cmd = {46.id = MCC_UPDATE_CMD,47.flags = CMD_WANT_SKB,48.data = { &mcc_update_cmd },49.len[0] = sizeof(mcc_update_cmd),50};51int ret;52u16 mcc;5354IWL_DEBUG_LAR(mld, "send MCC update to FW with '%c%c' src = %d\n",55alpha2[0], alpha2[1], src_id);5657ret = iwl_mld_send_cmd(mld, &cmd);58if (ret)59return ERR_PTR(ret);6061pkt = cmd.resp_pkt;6263resp_cp = iwl_mld_copy_mcc_resp(pkt);64if (IS_ERR(resp_cp))65goto exit;6667mcc = le16_to_cpu(resp_cp->mcc);6869IWL_FW_CHECK(mld, !mcc, "mcc can't be 0: %d\n", mcc);7071IWL_DEBUG_LAR(mld,72"MCC response status: 0x%x. new MCC: 0x%x ('%c%c')\n",73le32_to_cpu(resp_cp->status), mcc, mcc >> 8, mcc & 0xff);7475exit:76iwl_free_resp(&cmd);77return resp_cp;78}7980/* It is the caller's responsibility to free the pointer returned here */81struct ieee80211_regdomain *82iwl_mld_get_regdomain(struct iwl_mld *mld,83const char *alpha2,84enum iwl_mcc_source src_id,85bool *changed)86{87struct ieee80211_regdomain *regd = NULL;88struct iwl_mcc_update_resp_v8 *resp;89u8 resp_ver = iwl_fw_lookup_notif_ver(mld->fw, IWL_ALWAYS_LONG_GROUP,90MCC_UPDATE_CMD, 0);9192IWL_DEBUG_LAR(mld, "Getting regdomain data for %s from FW\n", alpha2);9394lockdep_assert_wiphy(mld->wiphy);9596resp = iwl_mld_update_mcc(mld, alpha2, src_id);97if (IS_ERR(resp)) {98IWL_DEBUG_LAR(mld, "Could not get update from FW %ld\n",99PTR_ERR(resp));100resp = NULL;101goto out;102}103104if (changed) {105u32 status = le32_to_cpu(resp->status);106107*changed = (status == MCC_RESP_NEW_CHAN_PROFILE ||108status == MCC_RESP_ILLEGAL);109}110IWL_DEBUG_LAR(mld, "MCC update response version: %d\n", resp_ver);111112regd = iwl_parse_nvm_mcc_info(mld->trans,113__le32_to_cpu(resp->n_channels),114resp->channels,115__le16_to_cpu(resp->mcc),116__le16_to_cpu(resp->geo_info),117le32_to_cpu(resp->cap), resp_ver);118119if (IS_ERR(regd)) {120IWL_DEBUG_LAR(mld, "Could not get parse update from FW %ld\n",121PTR_ERR(regd));122goto out;123}124125IWL_DEBUG_LAR(mld, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n",126regd->alpha2, regd->alpha2[0],127regd->alpha2[1], resp->source_id);128129mld->mcc_src = resp->source_id;130131/* FM is the earliest supported and later always do puncturing */132if (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_FM) {133if (!iwl_puncturing_is_allowed_in_bios(mld->bios_enable_puncturing,134le16_to_cpu(resp->mcc)))135ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING);136else137__clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING,138mld->hw->flags);139}140141out:142kfree(resp);143return regd;144}145146/* It is the caller's responsibility to free the pointer returned here */147static struct ieee80211_regdomain *148iwl_mld_get_current_regdomain(struct iwl_mld *mld,149bool *changed)150{151return iwl_mld_get_regdomain(mld, "ZZ",152MCC_SOURCE_GET_CURRENT, changed);153}154155void iwl_mld_update_changed_regdomain(struct iwl_mld *mld)156{157struct ieee80211_regdomain *regd;158bool changed;159160regd = iwl_mld_get_current_regdomain(mld, &changed);161162if (IS_ERR_OR_NULL(regd))163return;164165if (changed)166regulatory_set_wiphy_regd(mld->wiphy, regd);167kfree(regd);168}169170static int iwl_mld_apply_last_mcc(struct iwl_mld *mld,171const char *alpha2)172{173struct ieee80211_regdomain *regd;174u32 used_src;175bool changed;176int ret;177178/* save the last source in case we overwrite it below */179used_src = mld->mcc_src;180181/* Notify the firmware we support wifi location updates */182regd = iwl_mld_get_current_regdomain(mld, NULL);183if (!IS_ERR_OR_NULL(regd))184kfree(regd);185186/* Now set our last stored MCC and source */187regd = iwl_mld_get_regdomain(mld, alpha2, used_src,188&changed);189if (IS_ERR_OR_NULL(regd))190return -EIO;191192/* update cfg80211 if the regdomain was changed */193if (changed)194ret = regulatory_set_wiphy_regd_sync(mld->wiphy, regd);195else196ret = 0;197198kfree(regd);199return ret;200}201202int iwl_mld_init_mcc(struct iwl_mld *mld)203{204const struct ieee80211_regdomain *r;205struct ieee80211_regdomain *regd;206char mcc[3];207int retval;208209/* try to replay the last set MCC to FW */210r = wiphy_dereference(mld->wiphy, mld->wiphy->regd);211212if (r)213return iwl_mld_apply_last_mcc(mld, r->alpha2);214215regd = iwl_mld_get_current_regdomain(mld, NULL);216if (IS_ERR_OR_NULL(regd))217return -EIO;218219if (!iwl_bios_get_mcc(&mld->fwrt, mcc)) {220kfree(regd);221regd = iwl_mld_get_regdomain(mld, mcc, MCC_SOURCE_BIOS, NULL);222if (IS_ERR_OR_NULL(regd))223return -EIO;224}225226retval = regulatory_set_wiphy_regd_sync(mld->wiphy, regd);227228kfree(regd);229return retval;230}231232static void iwl_mld_find_assoc_vif_iterator(void *data, u8 *mac,233struct ieee80211_vif *vif)234{235bool *assoc = data;236237if (vif->type == NL80211_IFTYPE_STATION &&238vif->cfg.assoc)239*assoc = true;240}241242static bool iwl_mld_is_a_vif_assoc(struct iwl_mld *mld)243{244bool assoc = false;245246ieee80211_iterate_active_interfaces_atomic(mld->hw,247IEEE80211_IFACE_ITER_NORMAL,248iwl_mld_find_assoc_vif_iterator,249&assoc);250return assoc;251}252253void iwl_mld_handle_update_mcc(struct iwl_mld *mld, struct iwl_rx_packet *pkt)254{255struct iwl_mcc_chub_notif *notif = (void *)pkt->data;256enum iwl_mcc_source src;257char mcc[3];258struct ieee80211_regdomain *regd;259bool changed;260261lockdep_assert_wiphy(mld->wiphy);262263if (iwl_mld_is_a_vif_assoc(mld) &&264notif->source_id == MCC_SOURCE_WIFI) {265IWL_DEBUG_LAR(mld, "Ignore mcc update while associated\n");266return;267}268269mcc[0] = le16_to_cpu(notif->mcc) >> 8;270mcc[1] = le16_to_cpu(notif->mcc) & 0xff;271mcc[2] = '\0';272src = notif->source_id;273274IWL_DEBUG_LAR(mld,275"RX: received chub update mcc cmd (mcc '%s' src %d)\n",276mcc, src);277regd = iwl_mld_get_regdomain(mld, mcc, src, &changed);278if (IS_ERR_OR_NULL(regd))279return;280281if (changed)282regulatory_set_wiphy_regd(mld->hw->wiphy, regd);283kfree(regd);284}285286287