/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright ( C ) 2024 - 2025 Intel Corporation
*/
#ifndef __iwl_mld_h__
#define __iwl_mld_h__
#include <linux/leds.h>
#include <net/mac80211.h>
#include "iwl-trans.h"
#include "iwl-op-mode.h"
#include "fw/runtime.h"
#include "fw/notif-wait.h"
#include "fw/api/commands.h"
#include "fw/api/scan.h"
#include "fw/api/mac-cfg.h"
#include "fw/api/mac.h"
#include "fw/api/phy-ctxt.h"
#include "fw/api/datapath.h"
#include "fw/api/rx.h"
#include "fw/api/rs.h"
#include "fw/api/context.h"
#include "fw/api/coex.h"
#include "fw/api/location.h"
#include "fw/dbg.h"
#include "notif.h"
#include "scan.h"
#include "rx.h"
#include "thermal.h"
#include "low_latency.h"
#include "constants.h"
#include "ptp.h"
#include "time_sync.h"
#include "ftm-initiator.h"
/**
* DOC : Introduction
*
* iwlmld is an operation mode ( a . k . a . op_mode ) for Intel wireless devices .
* It is used for devices that ship after 2024 which typically support
* the WiFi - 7 features . MLD stands for multi - link device . Note that there are
* devices that do not support WiFi - 7 or even WiFi 6 E and yet use iwlmld , but
* the firmware APIs used in this driver are WiFi - 7 compatible .
*
* In the architecture of iwlwifi , an op_mode is a layer that translates
* mac80211 ' s APIs into commands for the firmware and , of course , notifications
* from the firmware to mac80211 ' s APIs . An op_mode must implement the
* interface defined in iwl - op - mode . h to interact with the transport layer
* which allows to send and receive data to the device , start the hardware ,
* etc . . .
*/
/**
* DOC : Locking policy
*
* iwlmld has a very simple locking policy : it doesn ' t have any mutexes . It
* relies on cfg80211 ' s wiphy - > mtx and takes the lock when needed . All the
* control flows originating from mac80211 already acquired the lock , so that
* part is trivial , but also notifications that are received from the firmware
* and handled asynchronously are handled only after having taken the lock .
* This is described in notif . c .
* There are spin_locks needed to synchronize with the data path , around the
* allocation of the queues , for example .
*/
/**
* DOC : Debugfs
*
* iwlmld adds its share of debugfs hooks and its handlers are synchronized
* with the wiphy_lock using wiphy_locked_debugfs . This avoids races against
* resources deletion while the debugfs hook is being used .
*/
/**
* DOC : Main resources
*
* iwlmld is designed with the life cycle of the resource in mind . The
* resources are :
*
* - struct iwl_mld ( matches mac80211 ' s struct ieee80211_hw )
*
* - struct iwl_mld_vif ( matches macu80211 ' s struct ieee80211_vif )
* iwl_mld_vif contains an array of pointers to struct iwl_mld_link
* which describe the links for this vif .
*
* - struct iwl_mld_sta ( matches mac80211 ' s struct ieee80211_sta )
* iwl_mld_sta contains an array of points to struct iwl_mld_link_sta
* which describes the link stations for this station
*
* Each object has properties that can survive a firmware reset or not .
* Asynchronous firmware notifications can declare themselves as dependent on a
* certain instance of those resources and that means that the notifications
* will be cancelled once the instance is destroyed .
*/
#define IWL_MLD_MAX_ADDRESSES 5
/**
* struct iwl_mld - MLD op mode
*
* @ fw_id_to_bss_conf : maps a fw id of a link to the corresponding
* ieee80211_bss_conf .
* @ fw_id_to_vif : maps a fw id of a MAC context to the corresponding
* ieee80211_vif . Mapping is valid only when the MAC exists in the fw .
* @ fw_id_to_txq : maps a fw id of a txq to the corresponding
* ieee80211_txq .
* @ used_phy_ids : a bitmap of the phy IDs used . If a bit is set , it means
* that the index of this bit is already used as a PHY id .
* @ num_igtks : the number if iGTKs that were sent to the FW .
* @ monitor : monitor related data
* @ monitor . on : does a monitor vif exist ( singleton hence bool )
* @ monitor . ampdu_ref : the id of the A - MPDU for sniffer
* @ monitor . ampdu_toggle : the state of the previous packet to track A - MPDU
* @ monitor . cur_aid : current association id tracked by the sniffer
* @ monitor . cur_bssid : current bssid tracked by the sniffer
* @ monitor . ptp_time : set the Rx mactime using the device ' s PTP clock time
* @ monitor . p80 : primary channel position relative to he whole bandwidth , in
* steps of 80 MHz
* @ fw_id_to_link_sta : maps a fw id of a sta to the corresponding
* ieee80211_link_sta . This is not cleaned up on restart since we want to
* preserve the fw sta ids during a restart ( for SN / PN restoring ) .
* FW ids of internal stations will be mapped to ERR_PTR , and will be
* re - allocated during a restart , so make sure to free it in restart
* cleanup using iwl_mld_free_internal_sta
* @ netdetect : indicates the FW is in suspend mode with netdetect configured
* @ p2p_device_vif : points to the p2p device vif if exists
* @ bt_is_active : indicates that BT is active
* @ dev : pointer to device struct . For printing purposes
* @ trans : pointer to the transport layer
* @ cfg : pointer to the device configuration
* @ fw : a pointer to the fw object
* @ hw : pointer to the hw object .
* @ wiphy : a pointer to the wiphy struct , for easier access to it .
* @ nvm_data : pointer to the nvm_data that includes all our capabilities
* @ fwrt : fw runtime data
* @ debugfs_dir : debugfs directory
* @ notif_wait : notification wait related data .
* @ async_handlers_list : a list of all async RX handlers . When a notifciation
* with an async handler is received , it is added to this list .
* When & async_handlers_wk runs - it runs these handlers one by one .
* @ async_handlers_lock : a lock for & async_handlers_list . Sync
* & async_handlers_wk and RX notifcation path .
* @ async_handlers_wk : A work to run all async RX handlers from
* & async_handlers_list .
* @ ct_kill_exit_wk : worker to exit thermal kill
* @ fw_status : bitmap of fw status bits
* @ running : true if the firmware is running
* @ do_not_dump_once : true if firmware dump must be prevented once
* @ in_d3 : indicates FW is in suspend mode and should be resumed
* @ resuming : indicates the driver is resuming from wowlan
* @ in_hw_restart : indicates that we are currently in restart flow .
* rather than restarted . Should be unset upon restart .
* @ radio_kill : bitmap of radio kill status
* @ radio_kill . hw : radio is killed by hw switch
* @ radio_kill . ct : radio is killed because the device it too hot
* @ power_budget_mw : maximum cTDP power budget as defined for this system and
* device
* @ addresses : device MAC addresses .
* @ scan : instance of the scan object
* @ channel_survey : channel survey information collected during scan
* @ wowlan : WoWLAN support data .
* @ debug_max_sleep : maximum sleep time in D3 ( for debug purposes )
* @ led : the led device
* @ mcc_src : the source id of the MCC , comes from the firmware
* @ bios_enable_puncturing : is puncturing enabled by bios
* @ fw_id_to_ba : maps a fw ( BA ) id to a corresponding Block Ack session data .
* @ num_rx_ba_sessions : tracks the number of active Rx Block Ack ( BA ) sessions .
* the driver ensures that new BA sessions are blocked once the maximum
* supported by the firmware is reached , preventing firmware asserts .
* @ rxq_sync : manages RX queue sync state
* @ txqs_to_add : a list of & ieee80211_txq ' s to allocate in & add_txqs_wk
* @ add_txqs_wk : a worker to allocate txqs .
* @ add_txqs_lock : to lock the & txqs_to_add list .
* @ error_recovery_buf : pointer to the recovery buffer that will be read
* from firmware upon fw / hw error and sent back to the firmware in
* reconfig flow ( after NIC reset ) .
* @ mcast_filter_cmd : pointer to the multicast filter command .
* @ mgmt_tx_ant : stores the last TX antenna index ; used for setting
* TX rate_n_flags for non - STA mgmt frames ( toggles on every TX failure ) .
* @ fw_rates_ver_3 : FW rates are in version 3
* @ low_latency : low - latency manager .
* @ tzone : thermal zone device ' s data
* @ cooling_dev : cooling device ' s related data
* @ ibss_manager : in IBSS mode ( only one vif can be active ) , indicates what
* firmware indicated about having transmitted the last beacon , i . e .
* being IBSS manager for that time and needing to respond to probe
* requests
* @ ptp_data : data of the PTP clock
* @ time_sync : time sync data .
* @ ftm_initiator : FTM initiator data
*/
struct iwl_mld {
/* Add here fields that need clean up on restart */
struct_group(zeroed_on_hw_restart,
struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINK_ID + 1 ];
struct ieee80211_vif __rcu *fw_id_to_vif[NUM_MAC_INDEX_DRIVER];
struct ieee80211_txq __rcu *fw_id_to_txq[IWL_MAX_TVQM_QUEUES];
u8 used_phy_ids: NUM_PHY_CTX;
u8 num_igtks;
struct {
bool on;
u32 ampdu_ref;
bool ampdu_toggle;
u8 p80;
#ifdef CONFIG_IWLWIFI_DEBUGFS
__le16 cur_aid;
u8 cur_bssid[ETH_ALEN];
bool ptp_time;
#endif
} monitor;
#ifdef CONFIG_PM_SLEEP
bool netdetect;
#endif /* CONFIG_PM_SLEEP */
struct ieee80211_vif *p2p_device_vif;
bool bt_is_active;
);
struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_STATION_COUNT_MAX];
/* And here fields that survive a fw restart */
struct device *dev;
struct iwl_trans *trans;
const struct iwl_rf_cfg *cfg;
const struct iwl_fw *fw;
struct ieee80211_hw *hw;
struct wiphy *wiphy;
struct iwl_nvm_data *nvm_data;
struct iwl_fw_runtime fwrt;
struct dentry *debugfs_dir;
struct iwl_notif_wait_data notif_wait;
struct list_head async_handlers_list;
spinlock_t async_handlers_lock;
struct wiphy_work async_handlers_wk;
struct wiphy_delayed_work ct_kill_exit_wk;
struct {
u32 running:1 ,
do_not_dump_once:1 ,
#ifdef CONFIG_PM_SLEEP
in_d3:1 ,
resuming:1 ,
#endif
in_hw_restart:1 ;
} fw_status;
struct {
u32 hw:1 ,
ct:1 ;
} radio_kill;
u32 power_budget_mw;
struct mac_address addresses[IWL_MLD_MAX_ADDRESSES];
struct iwl_mld_scan scan;
struct iwl_mld_survey *channel_survey;
#ifdef CONFIG_PM_SLEEP
struct wiphy_wowlan_support wowlan;
u32 debug_max_sleep;
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_IWLWIFI_LEDS
struct led_classdev led;
#endif
enum iwl_mcc_source mcc_src;
bool bios_enable_puncturing;
struct iwl_mld_baid_data __rcu *fw_id_to_ba[IWL_MAX_BAID];
u8 num_rx_ba_sessions;
struct iwl_mld_rx_queues_sync rxq_sync;
struct list_head txqs_to_add;
struct wiphy_work add_txqs_wk;
spinlock_t add_txqs_lock;
u8 *error_recovery_buf;
struct iwl_mcast_filter_cmd *mcast_filter_cmd;
u8 mgmt_tx_ant;
bool fw_rates_ver_3;
struct iwl_mld_low_latency low_latency;
bool ibss_manager;
#ifdef CONFIG_THERMAL
struct thermal_zone_device *tzone;
struct iwl_mld_cooling_device cooling_dev;
#endif
struct ptp_data ptp_data;
struct iwl_mld_time_sync_data __rcu *time_sync;
struct ftm_initiator_data ftm_initiator;
};
/* memset the part of the struct that requires cleanup on restart */
#define CLEANUP_STRUCT(_ptr) \
memset((void *)&(_ptr)->zeroed_on_hw_restart, 0 , \
sizeof ((_ptr)->zeroed_on_hw_restart))
/* Cleanup function for struct iwl_mld, will be called in restart */
static inline void
iwl_cleanup_mld(struct iwl_mld *mld)
{
CLEANUP_STRUCT(mld);
CLEANUP_STRUCT(&mld->scan);
#ifdef CONFIG_PM_SLEEP
mld->fw_status.in_d3 = false ;
#endif
iwl_mld_low_latency_restart_cleanup(mld);
}
enum iwl_power_scheme {
IWL_POWER_SCHEME_CAM = 1 ,
IWL_POWER_SCHEME_BPS,
};
/**
* struct iwl_mld_mod_params - module parameters for iwlmld
* @ power_scheme : one of enum iwl_power_scheme
*/
struct iwl_mld_mod_params {
int power_scheme;
};
extern struct iwl_mld_mod_params iwlmld_mod_params;
/* Extract MLD priv from op_mode */
#define IWL_OP_MODE_GET_MLD(_iwl_op_mode) \
((struct iwl_mld *)(_iwl_op_mode)->op_mode_specific)
#define IWL_MAC80211_GET_MLD(_hw) \
IWL_OP_MODE_GET_MLD((struct iwl_op_mode *)((_hw)->priv))
#ifdef CONFIG_IWLWIFI_DEBUGFS
void
iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir);
#else
static inline void
iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir)
{}
#endif
int iwl_mld_load_fw(struct iwl_mld *mld);
void iwl_mld_stop_fw(struct iwl_mld *mld);
int iwl_mld_start_fw(struct iwl_mld *mld);
void iwl_mld_send_recovery_cmd(struct iwl_mld *mld, u32 flags);
static inline void iwl_mld_set_ctkill(struct iwl_mld *mld, bool state)
{
mld->radio_kill.ct = state;
wiphy_rfkill_set_hw_state(mld->wiphy,
mld->radio_kill.hw || mld->radio_kill.ct);
}
static inline void iwl_mld_set_hwkill(struct iwl_mld *mld, bool state)
{
mld->radio_kill.hw = state;
wiphy_rfkill_set_hw_state(mld->wiphy,
mld->radio_kill.hw || mld->radio_kill.ct);
}
static inline u8 iwl_mld_get_valid_tx_ant(const struct iwl_mld *mld)
{
u8 tx_ant = mld->fw->valid_tx_ant;
if (mld->nvm_data && mld->nvm_data->valid_tx_ant)
tx_ant &= mld->nvm_data->valid_tx_ant;
return tx_ant;
}
static inline u8 iwl_mld_get_valid_rx_ant(const struct iwl_mld *mld)
{
u8 rx_ant = mld->fw->valid_rx_ant;
if (mld->nvm_data && mld->nvm_data->valid_rx_ant)
rx_ant &= mld->nvm_data->valid_rx_ant;
return rx_ant;
}
static inline u8 iwl_mld_nl80211_band_to_fw(enum nl80211_band band)
{
switch (band) {
case NL80211_BAND_2GHZ:
return PHY_BAND_24;
case NL80211_BAND_5GHZ:
return PHY_BAND_5;
case NL80211_BAND_6GHZ:
return PHY_BAND_6;
default :
WARN_ONCE(1 , "Unsupported band (%u)\n" , band);
return PHY_BAND_5;
}
}
static inline u8 iwl_mld_phy_band_to_nl80211(u8 phy_band)
{
switch (phy_band) {
case PHY_BAND_24:
return NL80211_BAND_2GHZ;
case PHY_BAND_5:
return NL80211_BAND_5GHZ;
case PHY_BAND_6:
return NL80211_BAND_6GHZ;
default :
WARN_ONCE(1 , "Unsupported phy band (%u)\n" , phy_band);
return NL80211_BAND_5GHZ;
}
}
static inline int
iwl_mld_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags,
enum nl80211_band band)
{
int format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
int rate = rate_n_flags & RATE_LEGACY_RATE_MSK;
bool is_lb = band == NL80211_BAND_2GHZ;
if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM)
return is_lb ? rate + IWL_FIRST_OFDM_RATE : rate;
/* CCK is not allowed in 5 GHz */
return is_lb ? rate : -1 ;
}
extern const struct ieee80211_ops iwl_mld_hw_ops;
/**
* enum iwl_rx_handler_context : context for Rx handler
* @ RX_HANDLER_SYNC : this means that it will be called in the Rx path
* which can ' t acquire the wiphy - > mutex .
* @ RX_HANDLER_ASYNC : If the handler needs to hold wiphy - > mutex
* ( and only in this case ! ) , it should be set as ASYNC . In that case ,
* it will be called from a worker with wiphy - > mutex held .
*/
enum iwl_rx_handler_context {
RX_HANDLER_SYNC,
RX_HANDLER_ASYNC,
};
/**
* struct iwl_rx_handler : handler for FW notification
* @ val_fn : input validation function .
* @ sizes : an array that mapps a version to the expected size .
* @ fn : the function is called when notification is handled
* @ cmd_id : command id
* @ n_sizes : number of elements in & sizes .
* @ context : see & iwl_rx_handler_context
* @ obj_type : the type of the object that this handler is related to .
* See & iwl_mld_object_type . Use IWL_MLD_OBJECT_TYPE_NONE if not related .
* @ cancel : function to cancel the notification . valid only if obj_type is not
* IWL_MLD_OBJECT_TYPE_NONE .
*/
struct iwl_rx_handler {
union {
bool (*val_fn)(struct iwl_mld *mld, struct iwl_rx_packet *pkt);
const struct iwl_notif_struct_size *sizes;
};
void (*fn)(struct iwl_mld *mld, struct iwl_rx_packet *pkt);
u16 cmd_id;
u8 n_sizes;
u8 context;
enum iwl_mld_object_type obj_type;
bool (*cancel)(struct iwl_mld *mld, struct iwl_rx_packet *pkt,
u32 obj_id);
};
/**
* struct iwl_notif_struct_size : map a notif ver to the expected size
*
* @ size : the size to expect
* @ ver : the version of the notification
*/
struct iwl_notif_struct_size {
u32 size:24 , ver:8 ;
};
#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
extern const struct iwl_hcmd_arr iwl_mld_groups[];
extern const unsigned int global_iwl_mld_goups_size;
extern const struct iwl_rx_handler iwl_mld_rx_handlers[];
extern const unsigned int iwl_mld_rx_handlers_num;
bool
iwl_mld_is_dup(struct iwl_mld *mld, struct ieee80211_sta *sta,
struct ieee80211_hdr *hdr,
const struct iwl_rx_mpdu_desc *mpdu_desc,
struct ieee80211_rx_status *rx_status, int queue);
void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans,
const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw,
struct ieee80211_hw *hw, struct dentry *dbgfs_dir);
#endif
#define IWL_MLD_INVALID_FW_ID 0 xff
#define IWL_MLD_ALLOC_FN(_type, _mac80211_type) \
static int \
iwl_mld_allocate_## _type## _fw_id(struct iwl_mld *mld, \
u8 *fw_id, \
struct ieee80211_## _mac80211_type *mac80211_ptr) \
{ \
u8 rand = IWL_MLD_DIS_RANDOM_FW_ID ? 0 : get_random_u8(); \
u8 arr_sz = ARRAY_SIZE(mld->fw_id_to_## _mac80211_type); \
if (__builtin_types_compatible_p(typeof(*mac80211_ptr), \
struct ieee80211_link_sta)) \
arr_sz = mld->fw->ucode_capa.num_stations; \
if (__builtin_types_compatible_p(typeof(*mac80211_ptr), \
struct ieee80211_bss_conf)) \
arr_sz = mld->fw->ucode_capa.num_links; \
for (int i = 0 ; i < arr_sz; i++) { \
u8 idx = (i + rand) % arr_sz; \
if (rcu_access_pointer(mld->fw_id_to_## _mac80211_type[idx])) \
continue ; \
IWL_DEBUG_INFO(mld, "Allocated at index %d / %d\n" , idx, arr_sz); \
*fw_id = idx; \
rcu_assign_pointer(mld->fw_id_to_## _mac80211_type[idx], mac80211_ptr); \
return 0 ; \
} \
return -ENOSPC; \
}
static inline struct ieee80211_bss_conf *
iwl_mld_fw_id_to_link_conf(struct iwl_mld *mld, u8 fw_link_id)
{
if (IWL_FW_CHECK(mld, fw_link_id >= mld->fw->ucode_capa.num_links,
"Invalid fw_link_id: %d\n" , fw_link_id))
return NULL;
return wiphy_dereference(mld->wiphy,
mld->fw_id_to_bss_conf[fw_link_id]);
}
#define MSEC_TO_TU(_msec) ((_msec) * 1000 / 1024 )
void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf,
struct dentry *dir);
void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_link_sta *link_sta,
struct dentry *dir);
/* Utilities */
static inline u8 iwl_mld_mac80211_ac_to_fw_tx_fifo(enum ieee80211_ac_numbers ac)
{
static const u8 mac80211_ac_to_fw_tx_fifo[] = {
IWL_BZ_EDCA_TX_FIFO_VO,
IWL_BZ_EDCA_TX_FIFO_VI,
IWL_BZ_EDCA_TX_FIFO_BE,
IWL_BZ_EDCA_TX_FIFO_BK,
IWL_BZ_TRIG_TX_FIFO_VO,
IWL_BZ_TRIG_TX_FIFO_VI,
IWL_BZ_TRIG_TX_FIFO_BE,
IWL_BZ_TRIG_TX_FIFO_BK,
};
return mac80211_ac_to_fw_tx_fifo[ac];
}
static inline u32
iwl_mld_get_lmac_id(struct iwl_mld *mld, enum nl80211_band band)
{
if (!fw_has_capa(&mld->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_CDB_SUPPORT) ||
band == NL80211_BAND_2GHZ)
return IWL_LMAC_24G_INDEX;
return IWL_LMAC_5G_INDEX;
}
/* Check if we had an error, but reconfig flow didn't start yet */
static inline bool iwl_mld_error_before_recovery(struct iwl_mld *mld)
{
return mld->fw_status.in_hw_restart &&
!iwl_trans_fw_running(mld->trans);
}
int iwl_mld_tdls_sta_count(struct iwl_mld *mld);
#endif /* __iwl_mld_h__ */
Messung V0.5 in Prozent C=93 H=98 G=95
¤ Dauer der Verarbeitung: 0.15 Sekunden
¤
*© Formatika GbR, Deutschland