/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */1/*2* Copyright (C) 2024-2025 Intel Corporation3*/4#ifndef __iwl_mld_h__5#define __iwl_mld_h__67#include <linux/leds.h>8#include <net/mac80211.h>910#include "iwl-trans.h"11#include "iwl-op-mode.h"12#include "fw/runtime.h"13#include "fw/notif-wait.h"14#include "fw/api/commands.h"15#include "fw/api/scan.h"16#include "fw/api/mac-cfg.h"17#include "fw/api/mac.h"18#include "fw/api/phy-ctxt.h"19#include "fw/api/datapath.h"20#include "fw/api/rx.h"21#include "fw/api/rs.h"22#include "fw/api/context.h"23#include "fw/api/coex.h"24#include "fw/api/location.h"2526#include "fw/dbg.h"2728#include "notif.h"29#include "scan.h"30#include "rx.h"31#include "thermal.h"32#include "low_latency.h"33#include "constants.h"34#include "ptp.h"35#include "time_sync.h"36#include "ftm-initiator.h"3738/**39* DOC: Introduction40*41* iwlmld is an operation mode (a.k.a. op_mode) for Intel wireless devices.42* It is used for devices that ship after 2024 which typically support43* the WiFi-7 features. MLD stands for multi-link device. Note that there are44* devices that do not support WiFi-7 or even WiFi 6E and yet use iwlmld, but45* the firmware APIs used in this driver are WiFi-7 compatible.46*47* In the architecture of iwlwifi, an op_mode is a layer that translates48* mac80211's APIs into commands for the firmware and, of course, notifications49* from the firmware to mac80211's APIs. An op_mode must implement the50* interface defined in iwl-op-mode.h to interact with the transport layer51* which allows to send and receive data to the device, start the hardware,52* etc...53*/5455/**56* DOC: Locking policy57*58* iwlmld has a very simple locking policy: it doesn't have any mutexes. It59* relies on cfg80211's wiphy->mtx and takes the lock when needed. All the60* control flows originating from mac80211 already acquired the lock, so that61* part is trivial, but also notifications that are received from the firmware62* and handled asynchronously are handled only after having taken the lock.63* This is described in notif.c.64* There are spin_locks needed to synchronize with the data path, around the65* allocation of the queues, for example.66*/6768/**69* DOC: Debugfs70*71* iwlmld adds its share of debugfs hooks and its handlers are synchronized72* with the wiphy_lock using wiphy_locked_debugfs. This avoids races against73* resources deletion while the debugfs hook is being used.74*/7576/**77* DOC: Main resources78*79* iwlmld is designed with the life cycle of the resource in mind. The80* resources are:81*82* - struct iwl_mld (matches mac80211's struct ieee80211_hw)83*84* - struct iwl_mld_vif (matches macu80211's struct ieee80211_vif)85* iwl_mld_vif contains an array of pointers to struct iwl_mld_link86* which describe the links for this vif.87*88* - struct iwl_mld_sta (matches mac80211's struct ieee80211_sta)89* iwl_mld_sta contains an array of points to struct iwl_mld_link_sta90* which describes the link stations for this station91*92* Each object has properties that can survive a firmware reset or not.93* Asynchronous firmware notifications can declare themselves as dependent on a94* certain instance of those resources and that means that the notifications95* will be cancelled once the instance is destroyed.96*/9798#define IWL_MLD_MAX_ADDRESSES 599100/**101* struct iwl_mld - MLD op mode102*103* @fw_id_to_bss_conf: maps a fw id of a link to the corresponding104* ieee80211_bss_conf.105* @fw_id_to_vif: maps a fw id of a MAC context to the corresponding106* ieee80211_vif. Mapping is valid only when the MAC exists in the fw.107* @fw_id_to_txq: maps a fw id of a txq to the corresponding108* ieee80211_txq.109* @used_phy_ids: a bitmap of the phy IDs used. If a bit is set, it means110* that the index of this bit is already used as a PHY id.111* @num_igtks: the number if iGTKs that were sent to the FW.112* @monitor: monitor related data113* @monitor.on: does a monitor vif exist (singleton hence bool)114* @monitor.ampdu_ref: the id of the A-MPDU for sniffer115* @monitor.ampdu_toggle: the state of the previous packet to track A-MPDU116* @monitor.cur_aid: current association id tracked by the sniffer117* @monitor.cur_bssid: current bssid tracked by the sniffer118* @monitor.ptp_time: set the Rx mactime using the device's PTP clock time119* @monitor.p80: primary channel position relative to he whole bandwidth, in120* steps of 80 MHz121* @fw_id_to_link_sta: maps a fw id of a sta to the corresponding122* ieee80211_link_sta. This is not cleaned up on restart since we want to123* preserve the fw sta ids during a restart (for SN/PN restoring).124* FW ids of internal stations will be mapped to ERR_PTR, and will be125* re-allocated during a restart, so make sure to free it in restart126* cleanup using iwl_mld_free_internal_sta127* @netdetect: indicates the FW is in suspend mode with netdetect configured128* @p2p_device_vif: points to the p2p device vif if exists129* @bt_is_active: indicates that BT is active130* @dev: pointer to device struct. For printing purposes131* @trans: pointer to the transport layer132* @cfg: pointer to the device configuration133* @fw: a pointer to the fw object134* @hw: pointer to the hw object.135* @wiphy: a pointer to the wiphy struct, for easier access to it.136* @nvm_data: pointer to the nvm_data that includes all our capabilities137* @fwrt: fw runtime data138* @debugfs_dir: debugfs directory139* @notif_wait: notification wait related data.140* @async_handlers_list: a list of all async RX handlers. When a notifciation141* with an async handler is received, it is added to this list.142* When &async_handlers_wk runs - it runs these handlers one by one.143* @async_handlers_lock: a lock for &async_handlers_list. Sync144* &async_handlers_wk and RX notifcation path.145* @async_handlers_wk: A work to run all async RX handlers from146* &async_handlers_list.147* @ct_kill_exit_wk: worker to exit thermal kill148* @fw_status: bitmap of fw status bits149* @running: true if the firmware is running150* @do_not_dump_once: true if firmware dump must be prevented once151* @in_d3: indicates FW is in suspend mode and should be resumed152* @resuming: indicates the driver is resuming from wowlan153* @in_hw_restart: indicates that we are currently in restart flow.154* rather than restarted. Should be unset upon restart.155* @radio_kill: bitmap of radio kill status156* @radio_kill.hw: radio is killed by hw switch157* @radio_kill.ct: radio is killed because the device it too hot158* @power_budget_mw: maximum cTDP power budget as defined for this system and159* device160* @addresses: device MAC addresses.161* @scan: instance of the scan object162* @channel_survey: channel survey information collected during scan163* @wowlan: WoWLAN support data.164* @debug_max_sleep: maximum sleep time in D3 (for debug purposes)165* @led: the led device166* @mcc_src: the source id of the MCC, comes from the firmware167* @bios_enable_puncturing: is puncturing enabled by bios168* @fw_id_to_ba: maps a fw (BA) id to a corresponding Block Ack session data.169* @num_rx_ba_sessions: tracks the number of active Rx Block Ack (BA) sessions.170* the driver ensures that new BA sessions are blocked once the maximum171* supported by the firmware is reached, preventing firmware asserts.172* @rxq_sync: manages RX queue sync state173* @txqs_to_add: a list of &ieee80211_txq's to allocate in &add_txqs_wk174* @add_txqs_wk: a worker to allocate txqs.175* @add_txqs_lock: to lock the &txqs_to_add list.176* @error_recovery_buf: pointer to the recovery buffer that will be read177* from firmware upon fw/hw error and sent back to the firmware in178* reconfig flow (after NIC reset).179* @mcast_filter_cmd: pointer to the multicast filter command.180* @mgmt_tx_ant: stores the last TX antenna index; used for setting181* TX rate_n_flags for non-STA mgmt frames (toggles on every TX failure).182* @fw_rates_ver_3: FW rates are in version 3183* @low_latency: low-latency manager.184* @tzone: thermal zone device's data185* @cooling_dev: cooling device's related data186* @ibss_manager: in IBSS mode (only one vif can be active), indicates what187* firmware indicated about having transmitted the last beacon, i.e.188* being IBSS manager for that time and needing to respond to probe189* requests190* @ptp_data: data of the PTP clock191* @time_sync: time sync data.192* @ftm_initiator: FTM initiator data193*/194struct iwl_mld {195/* Add here fields that need clean up on restart */196struct_group(zeroed_on_hw_restart,197struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINK_ID + 1];198struct ieee80211_vif __rcu *fw_id_to_vif[NUM_MAC_INDEX_DRIVER];199struct ieee80211_txq __rcu *fw_id_to_txq[IWL_MAX_TVQM_QUEUES];200u8 used_phy_ids: NUM_PHY_CTX;201u8 num_igtks;202struct {203bool on;204u32 ampdu_ref;205bool ampdu_toggle;206u8 p80;207#ifdef CONFIG_IWLWIFI_DEBUGFS208__le16 cur_aid;209u8 cur_bssid[ETH_ALEN];210bool ptp_time;211#endif212} monitor;213#ifdef CONFIG_PM_SLEEP214bool netdetect;215#endif /* CONFIG_PM_SLEEP */216struct ieee80211_vif *p2p_device_vif;217bool bt_is_active;218);219struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_STATION_COUNT_MAX];220/* And here fields that survive a fw restart */221struct device *dev;222struct iwl_trans *trans;223const struct iwl_rf_cfg *cfg;224const struct iwl_fw *fw;225struct ieee80211_hw *hw;226struct wiphy *wiphy;227struct iwl_nvm_data *nvm_data;228struct iwl_fw_runtime fwrt;229struct dentry *debugfs_dir;230struct iwl_notif_wait_data notif_wait;231struct list_head async_handlers_list;232spinlock_t async_handlers_lock;233struct wiphy_work async_handlers_wk;234struct wiphy_delayed_work ct_kill_exit_wk;235236struct {237u32 running:1,238do_not_dump_once:1,239#ifdef CONFIG_PM_SLEEP240in_d3:1,241resuming:1,242#endif243in_hw_restart:1;244245} fw_status;246247struct {248u32 hw:1,249ct:1;250} radio_kill;251252u32 power_budget_mw;253254struct mac_address addresses[IWL_MLD_MAX_ADDRESSES];255struct iwl_mld_scan scan;256struct iwl_mld_survey *channel_survey;257#ifdef CONFIG_PM_SLEEP258struct wiphy_wowlan_support wowlan;259u32 debug_max_sleep;260#endif /* CONFIG_PM_SLEEP */261#ifdef CONFIG_IWLWIFI_LEDS262struct led_classdev led;263#endif264enum iwl_mcc_source mcc_src;265bool bios_enable_puncturing;266267struct iwl_mld_baid_data __rcu *fw_id_to_ba[IWL_MAX_BAID];268u8 num_rx_ba_sessions;269270struct iwl_mld_rx_queues_sync rxq_sync;271272struct list_head txqs_to_add;273struct wiphy_work add_txqs_wk;274spinlock_t add_txqs_lock;275276u8 *error_recovery_buf;277struct iwl_mcast_filter_cmd *mcast_filter_cmd;278279u8 mgmt_tx_ant;280281bool fw_rates_ver_3;282283struct iwl_mld_low_latency low_latency;284285bool ibss_manager;286#ifdef CONFIG_THERMAL287struct thermal_zone_device *tzone;288struct iwl_mld_cooling_device cooling_dev;289#endif290291struct ptp_data ptp_data;292293struct iwl_mld_time_sync_data __rcu *time_sync;294295struct ftm_initiator_data ftm_initiator;296};297298/* memset the part of the struct that requires cleanup on restart */299#define CLEANUP_STRUCT(_ptr) \300memset((void *)&(_ptr)->zeroed_on_hw_restart, 0, \301sizeof((_ptr)->zeroed_on_hw_restart))302303/* Cleanup function for struct iwl_mld, will be called in restart */304static inline void305iwl_cleanup_mld(struct iwl_mld *mld)306{307CLEANUP_STRUCT(mld);308CLEANUP_STRUCT(&mld->scan);309310#ifdef CONFIG_PM_SLEEP311mld->fw_status.in_d3 = false;312#endif313314iwl_mld_low_latency_restart_cleanup(mld);315}316317enum iwl_power_scheme {318IWL_POWER_SCHEME_CAM = 1,319IWL_POWER_SCHEME_BPS,320};321322/**323* struct iwl_mld_mod_params - module parameters for iwlmld324* @power_scheme: one of enum iwl_power_scheme325*/326struct iwl_mld_mod_params {327int power_scheme;328};329330extern struct iwl_mld_mod_params iwlmld_mod_params;331332/* Extract MLD priv from op_mode */333#define IWL_OP_MODE_GET_MLD(_iwl_op_mode) \334((struct iwl_mld *)(_iwl_op_mode)->op_mode_specific)335336#define IWL_MAC80211_GET_MLD(_hw) \337IWL_OP_MODE_GET_MLD((struct iwl_op_mode *)((_hw)->priv))338339#ifdef CONFIG_IWLWIFI_DEBUGFS340void341iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir);342#else343static inline void344iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir)345{}346#endif347348int iwl_mld_load_fw(struct iwl_mld *mld);349void iwl_mld_stop_fw(struct iwl_mld *mld);350int iwl_mld_start_fw(struct iwl_mld *mld);351void iwl_mld_send_recovery_cmd(struct iwl_mld *mld, u32 flags);352353static inline void iwl_mld_set_ctkill(struct iwl_mld *mld, bool state)354{355mld->radio_kill.ct = state;356357wiphy_rfkill_set_hw_state(mld->wiphy,358mld->radio_kill.hw || mld->radio_kill.ct);359}360361static inline void iwl_mld_set_hwkill(struct iwl_mld *mld, bool state)362{363mld->radio_kill.hw = state;364365wiphy_rfkill_set_hw_state(mld->wiphy,366mld->radio_kill.hw || mld->radio_kill.ct);367}368369static inline u8 iwl_mld_get_valid_tx_ant(const struct iwl_mld *mld)370{371u8 tx_ant = mld->fw->valid_tx_ant;372373if (mld->nvm_data && mld->nvm_data->valid_tx_ant)374tx_ant &= mld->nvm_data->valid_tx_ant;375376return tx_ant;377}378379static inline u8 iwl_mld_get_valid_rx_ant(const struct iwl_mld *mld)380{381u8 rx_ant = mld->fw->valid_rx_ant;382383if (mld->nvm_data && mld->nvm_data->valid_rx_ant)384rx_ant &= mld->nvm_data->valid_rx_ant;385386return rx_ant;387}388389static inline u8 iwl_mld_nl80211_band_to_fw(enum nl80211_band band)390{391switch (band) {392case NL80211_BAND_2GHZ:393return PHY_BAND_24;394case NL80211_BAND_5GHZ:395return PHY_BAND_5;396case NL80211_BAND_6GHZ:397return PHY_BAND_6;398default:399WARN_ONCE(1, "Unsupported band (%u)\n", band);400return PHY_BAND_5;401}402}403404static inline u8 iwl_mld_phy_band_to_nl80211(u8 phy_band)405{406switch (phy_band) {407case PHY_BAND_24:408return NL80211_BAND_2GHZ;409case PHY_BAND_5:410return NL80211_BAND_5GHZ;411case PHY_BAND_6:412return NL80211_BAND_6GHZ;413default:414WARN_ONCE(1, "Unsupported phy band (%u)\n", phy_band);415return NL80211_BAND_5GHZ;416}417}418419static inline int420iwl_mld_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags,421enum nl80211_band band)422{423int format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;424int rate = rate_n_flags & RATE_LEGACY_RATE_MSK;425bool is_lb = band == NL80211_BAND_2GHZ;426427if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM)428return is_lb ? rate + IWL_FIRST_OFDM_RATE : rate;429430/* CCK is not allowed in 5 GHz */431return is_lb ? rate : -1;432}433434extern const struct ieee80211_ops iwl_mld_hw_ops;435436/**437* enum iwl_rx_handler_context: context for Rx handler438* @RX_HANDLER_SYNC: this means that it will be called in the Rx path439* which can't acquire the wiphy->mutex.440* @RX_HANDLER_ASYNC: If the handler needs to hold wiphy->mutex441* (and only in this case!), it should be set as ASYNC. In that case,442* it will be called from a worker with wiphy->mutex held.443*/444enum iwl_rx_handler_context {445RX_HANDLER_SYNC,446RX_HANDLER_ASYNC,447};448449/**450* struct iwl_rx_handler: handler for FW notification451* @val_fn: input validation function.452* @sizes: an array that mapps a version to the expected size.453* @fn: the function is called when notification is handled454* @cmd_id: command id455* @n_sizes: number of elements in &sizes.456* @context: see &iwl_rx_handler_context457* @obj_type: the type of the object that this handler is related to.458* See &iwl_mld_object_type. Use IWL_MLD_OBJECT_TYPE_NONE if not related.459* @cancel: function to cancel the notification. valid only if obj_type is not460* IWL_MLD_OBJECT_TYPE_NONE.461*/462struct iwl_rx_handler {463union {464bool (*val_fn)(struct iwl_mld *mld, struct iwl_rx_packet *pkt);465const struct iwl_notif_struct_size *sizes;466};467void (*fn)(struct iwl_mld *mld, struct iwl_rx_packet *pkt);468u16 cmd_id;469u8 n_sizes;470u8 context;471enum iwl_mld_object_type obj_type;472bool (*cancel)(struct iwl_mld *mld, struct iwl_rx_packet *pkt,473u32 obj_id);474};475476/**477* struct iwl_notif_struct_size: map a notif ver to the expected size478*479* @size: the size to expect480* @ver: the version of the notification481*/482struct iwl_notif_struct_size {483u32 size:24, ver:8;484};485486#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)487extern const struct iwl_hcmd_arr iwl_mld_groups[];488extern const unsigned int global_iwl_mld_goups_size;489extern const struct iwl_rx_handler iwl_mld_rx_handlers[];490extern const unsigned int iwl_mld_rx_handlers_num;491492bool493iwl_mld_is_dup(struct iwl_mld *mld, struct ieee80211_sta *sta,494struct ieee80211_hdr *hdr,495const struct iwl_rx_mpdu_desc *mpdu_desc,496struct ieee80211_rx_status *rx_status, int queue);497498void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans,499const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw,500struct ieee80211_hw *hw, struct dentry *dbgfs_dir);501#endif502503#define IWL_MLD_INVALID_FW_ID 0xff504505#define IWL_MLD_ALLOC_FN(_type, _mac80211_type) \506static int \507iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld, \508u8 *fw_id, \509struct ieee80211_##_mac80211_type *mac80211_ptr) \510{ \511u8 rand = IWL_MLD_DIS_RANDOM_FW_ID ? 0 : get_random_u8(); \512u8 arr_sz = ARRAY_SIZE(mld->fw_id_to_##_mac80211_type); \513if (__builtin_types_compatible_p(typeof(*mac80211_ptr), \514struct ieee80211_link_sta)) \515arr_sz = mld->fw->ucode_capa.num_stations; \516if (__builtin_types_compatible_p(typeof(*mac80211_ptr), \517struct ieee80211_bss_conf)) \518arr_sz = mld->fw->ucode_capa.num_links; \519for (int i = 0; i < arr_sz; i++) { \520u8 idx = (i + rand) % arr_sz; \521if (rcu_access_pointer(mld->fw_id_to_##_mac80211_type[idx])) \522continue; \523IWL_DEBUG_INFO(mld, "Allocated at index %d / %d\n", idx, arr_sz); \524*fw_id = idx; \525rcu_assign_pointer(mld->fw_id_to_##_mac80211_type[idx], mac80211_ptr); \526return 0; \527} \528return -ENOSPC; \529}530531static inline struct ieee80211_bss_conf *532iwl_mld_fw_id_to_link_conf(struct iwl_mld *mld, u8 fw_link_id)533{534if (IWL_FW_CHECK(mld, fw_link_id >= mld->fw->ucode_capa.num_links,535"Invalid fw_link_id: %d\n", fw_link_id))536return NULL;537538return wiphy_dereference(mld->wiphy,539mld->fw_id_to_bss_conf[fw_link_id]);540}541542#define MSEC_TO_TU(_msec) ((_msec) * 1000 / 1024)543544void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw,545struct ieee80211_vif *vif);546void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw,547struct ieee80211_vif *vif,548struct ieee80211_bss_conf *link_conf,549struct dentry *dir);550void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw,551struct ieee80211_vif *vif,552struct ieee80211_link_sta *link_sta,553struct dentry *dir);554555/* Utilities */556557static inline u8 iwl_mld_mac80211_ac_to_fw_tx_fifo(enum ieee80211_ac_numbers ac)558{559static const u8 mac80211_ac_to_fw_tx_fifo[] = {560IWL_BZ_EDCA_TX_FIFO_VO,561IWL_BZ_EDCA_TX_FIFO_VI,562IWL_BZ_EDCA_TX_FIFO_BE,563IWL_BZ_EDCA_TX_FIFO_BK,564IWL_BZ_TRIG_TX_FIFO_VO,565IWL_BZ_TRIG_TX_FIFO_VI,566IWL_BZ_TRIG_TX_FIFO_BE,567IWL_BZ_TRIG_TX_FIFO_BK,568};569return mac80211_ac_to_fw_tx_fifo[ac];570}571572static inline u32573iwl_mld_get_lmac_id(struct iwl_mld *mld, enum nl80211_band band)574{575if (!fw_has_capa(&mld->fw->ucode_capa,576IWL_UCODE_TLV_CAPA_CDB_SUPPORT) ||577band == NL80211_BAND_2GHZ)578return IWL_LMAC_24G_INDEX;579return IWL_LMAC_5G_INDEX;580}581582/* Check if we had an error, but reconfig flow didn't start yet */583static inline bool iwl_mld_error_before_recovery(struct iwl_mld *mld)584{585return mld->fw_status.in_hw_restart &&586!iwl_trans_fw_running(mld->trans);587}588589int iwl_mld_tdls_sta_count(struct iwl_mld *mld);590591#endif /* __iwl_mld_h__ */592593594