mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-21 06:50:25 +00:00

The firmware is able to collect channel statistics when doing passive scans. Enable the flag when doing a passive scan on an AP interface and collect the survey information. Signed-off-by: Benjamin Berg <benjamin.berg@intel.com> Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com> Link: https://patch.msgid.link/20250723094230.a659ef1b1fd8.I24a9a0383327c231f36be170968bc7bac801f9f2@changeid
592 lines
19 KiB
C
592 lines
19 KiB
C
/* 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 6E 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 0xff
|
|
|
|
#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__ */
|