1d1e879ecSMiri Korenblit /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
2d1e879ecSMiri Korenblit /*
3d1e879ecSMiri Korenblit * Copyright (C) 2024-2025 Intel Corporation
4d1e879ecSMiri Korenblit */
5d1e879ecSMiri Korenblit #ifndef __iwl_mld_h__
6d1e879ecSMiri Korenblit #define __iwl_mld_h__
7d1e879ecSMiri Korenblit
8d1e879ecSMiri Korenblit #include <linux/leds.h>
9d1e879ecSMiri Korenblit #include <net/mac80211.h>
10d1e879ecSMiri Korenblit
11d1e879ecSMiri Korenblit #include "iwl-trans.h"
12d1e879ecSMiri Korenblit #include "iwl-op-mode.h"
13d1e879ecSMiri Korenblit #include "fw/runtime.h"
14d1e879ecSMiri Korenblit #include "fw/notif-wait.h"
15d1e879ecSMiri Korenblit #include "fw/api/commands.h"
16d1e879ecSMiri Korenblit #include "fw/api/scan.h"
17d1e879ecSMiri Korenblit #include "fw/api/mac-cfg.h"
18d1e879ecSMiri Korenblit #include "fw/api/mac.h"
19d1e879ecSMiri Korenblit #include "fw/api/phy-ctxt.h"
20d1e879ecSMiri Korenblit #include "fw/api/datapath.h"
21d1e879ecSMiri Korenblit #include "fw/api/rx.h"
22d1e879ecSMiri Korenblit #include "fw/api/rs.h"
23d1e879ecSMiri Korenblit #include "fw/api/context.h"
24d1e879ecSMiri Korenblit #include "fw/api/coex.h"
25d1e879ecSMiri Korenblit #include "fw/api/location.h"
26d1e879ecSMiri Korenblit
27d1e879ecSMiri Korenblit #include "fw/dbg.h"
28d1e879ecSMiri Korenblit
29d1e879ecSMiri Korenblit #include "notif.h"
30d1e879ecSMiri Korenblit #include "scan.h"
31d1e879ecSMiri Korenblit #include "rx.h"
32d1e879ecSMiri Korenblit #include "thermal.h"
33d1e879ecSMiri Korenblit #include "low_latency.h"
34d1e879ecSMiri Korenblit #include "constants.h"
35d1e879ecSMiri Korenblit #include "ptp.h"
36d1e879ecSMiri Korenblit #include "time_sync.h"
37*32c33a4dSAvraham Stern #include "ftm-initiator.h"
38d1e879ecSMiri Korenblit
39d1e879ecSMiri Korenblit /**
40d1e879ecSMiri Korenblit * DOC: Introduction
41d1e879ecSMiri Korenblit *
42d1e879ecSMiri Korenblit * iwlmld is an operation mode (a.k.a. op_mode) for Intel wireless devices.
43d1e879ecSMiri Korenblit * It is used for devices that ship after 2024 which typically support
44d1e879ecSMiri Korenblit * the WiFi-7 features. MLD stands for multi-link device. Note that there are
45d1e879ecSMiri Korenblit * devices that do not support WiFi-7 or even WiFi 6E and yet use iwlmld, but
46d1e879ecSMiri Korenblit * the firmware APIs used in this driver are WiFi-7 compatible.
47d1e879ecSMiri Korenblit *
48d1e879ecSMiri Korenblit * In the architecture of iwlwifi, an op_mode is a layer that translates
49d1e879ecSMiri Korenblit * mac80211's APIs into commands for the firmware and, of course, notifications
50d1e879ecSMiri Korenblit * from the firmware to mac80211's APIs. An op_mode must implement the
51d1e879ecSMiri Korenblit * interface defined in iwl-op-mode.h to interact with the transport layer
52d1e879ecSMiri Korenblit * which allows to send and receive data to the device, start the hardware,
53d1e879ecSMiri Korenblit * etc...
54d1e879ecSMiri Korenblit */
55d1e879ecSMiri Korenblit
56d1e879ecSMiri Korenblit /**
57d1e879ecSMiri Korenblit * DOC: Locking policy
58d1e879ecSMiri Korenblit *
59d1e879ecSMiri Korenblit * iwlmld has a very simple locking policy: it doesn't have any mutexes. It
60d1e879ecSMiri Korenblit * relies on cfg80211's wiphy->mtx and takes the lock when needed. All the
61d1e879ecSMiri Korenblit * control flows originating from mac80211 already acquired the lock, so that
62d1e879ecSMiri Korenblit * part is trivial, but also notifications that are received from the firmware
63d1e879ecSMiri Korenblit * and handled asynchronously are handled only after having taken the lock.
64d1e879ecSMiri Korenblit * This is described in notif.c.
65d1e879ecSMiri Korenblit * There are spin_locks needed to synchronize with the data path, around the
66d1e879ecSMiri Korenblit * allocation of the queues, for example.
67d1e879ecSMiri Korenblit */
68d1e879ecSMiri Korenblit
69d1e879ecSMiri Korenblit /**
70d1e879ecSMiri Korenblit * DOC: Debugfs
71d1e879ecSMiri Korenblit *
72d1e879ecSMiri Korenblit * iwlmld adds its share of debugfs hooks and its handlers are synchronized
73d1e879ecSMiri Korenblit * with the wiphy_lock using wiphy_locked_debugfs. This avoids races against
74d1e879ecSMiri Korenblit * resources deletion while the debugfs hook is being used.
75d1e879ecSMiri Korenblit */
76d1e879ecSMiri Korenblit
77d1e879ecSMiri Korenblit /**
78d1e879ecSMiri Korenblit * DOC: Main resources
79d1e879ecSMiri Korenblit *
80d1e879ecSMiri Korenblit * iwlmld is designed with the life cycle of the resource in mind. The
81d1e879ecSMiri Korenblit * resources are:
82d1e879ecSMiri Korenblit *
83d1e879ecSMiri Korenblit * - struct iwl_mld (matches mac80211's struct ieee80211_hw)
84d1e879ecSMiri Korenblit *
85d1e879ecSMiri Korenblit * - struct iwl_mld_vif (matches macu80211's struct ieee80211_vif)
86d1e879ecSMiri Korenblit * iwl_mld_vif contains an array of pointers to struct iwl_mld_link
87d1e879ecSMiri Korenblit * which describe the links for this vif.
88d1e879ecSMiri Korenblit *
89d1e879ecSMiri Korenblit * - struct iwl_mld_sta (matches mac80211's struct ieee80211_sta)
90d1e879ecSMiri Korenblit * iwl_mld_sta contains an array of points to struct iwl_mld_link_sta
91d1e879ecSMiri Korenblit * which describes the link stations for this station
92d1e879ecSMiri Korenblit *
93d1e879ecSMiri Korenblit * Each object has properties that can survive a firmware reset or not.
94d1e879ecSMiri Korenblit * Asynchronous firmware notifications can declare themselves as dependent on a
95d1e879ecSMiri Korenblit * certain instance of those resources and that means that the notifications
96d1e879ecSMiri Korenblit * will be cancelled once the instance is destroyed.
97d1e879ecSMiri Korenblit */
98d1e879ecSMiri Korenblit
99d1e879ecSMiri Korenblit #define IWL_MLD_MAX_ADDRESSES 5
100d1e879ecSMiri Korenblit
101d1e879ecSMiri Korenblit /**
102d1e879ecSMiri Korenblit * struct iwl_mld - MLD op mode
103d1e879ecSMiri Korenblit *
104d1e879ecSMiri Korenblit * @fw_id_to_bss_conf: maps a fw id of a link to the corresponding
105d1e879ecSMiri Korenblit * ieee80211_bss_conf.
106d1e879ecSMiri Korenblit * @fw_id_to_vif: maps a fw id of a MAC context to the corresponding
107d1e879ecSMiri Korenblit * ieee80211_vif. Mapping is valid only when the MAC exists in the fw.
108d1e879ecSMiri Korenblit * @fw_id_to_txq: maps a fw id of a txq to the corresponding
109d1e879ecSMiri Korenblit * ieee80211_txq.
110d1e879ecSMiri Korenblit * @used_phy_ids: a bitmap of the phy IDs used. If a bit is set, it means
111d1e879ecSMiri Korenblit * that the index of this bit is already used as a PHY id.
112d1e879ecSMiri Korenblit * @num_igtks: the number if iGTKs that were sent to the FW.
113d1e879ecSMiri Korenblit * @monitor: monitor related data
114d1e879ecSMiri Korenblit * @monitor.on: does a monitor vif exist (singleton hence bool)
115d1e879ecSMiri Korenblit * @monitor.ampdu_ref: the id of the A-MPDU for sniffer
116d1e879ecSMiri Korenblit * @monitor.ampdu_toggle: the state of the previous packet to track A-MPDU
117d1e879ecSMiri Korenblit * @monitor.cur_aid: current association id tracked by the sniffer
118d1e879ecSMiri Korenblit * @monitor.cur_bssid: current bssid tracked by the sniffer
119d1e879ecSMiri Korenblit * @monitor.ptp_time: set the Rx mactime using the device's PTP clock time
120d1e879ecSMiri Korenblit * @monitor.p80: primary channel position relative to he whole bandwidth, in
121d1e879ecSMiri Korenblit * steps of 80 MHz
122d1e879ecSMiri Korenblit * @fw_id_to_link_sta: maps a fw id of a sta to the corresponding
123d1e879ecSMiri Korenblit * ieee80211_link_sta. This is not cleaned up on restart since we want to
124d1e879ecSMiri Korenblit * preserve the fw sta ids during a restart (for SN/PN restoring).
125d1e879ecSMiri Korenblit * FW ids of internal stations will be mapped to ERR_PTR, and will be
126d1e879ecSMiri Korenblit * re-allocated during a restart, so make sure to free it in restart
127d1e879ecSMiri Korenblit * cleanup using iwl_mld_free_internal_sta
128d1e879ecSMiri Korenblit * @netdetect: indicates the FW is in suspend mode with netdetect configured
129d1e879ecSMiri Korenblit * @p2p_device_vif: points to the p2p device vif if exists
130d1e879ecSMiri Korenblit * @dev: pointer to device struct. For printing purposes
131d1e879ecSMiri Korenblit * @trans: pointer to the transport layer
132d1e879ecSMiri Korenblit * @cfg: pointer to the device configuration
133d1e879ecSMiri Korenblit * @fw: a pointer to the fw object
134d1e879ecSMiri Korenblit * @hw: pointer to the hw object.
135d1e879ecSMiri Korenblit * @wiphy: a pointer to the wiphy struct, for easier access to it.
136d1e879ecSMiri Korenblit * @nvm_data: pointer to the nvm_data that includes all our capabilities
137d1e879ecSMiri Korenblit * @fwrt: fw runtime data
138d1e879ecSMiri Korenblit * @debugfs_dir: debugfs directory
139d1e879ecSMiri Korenblit * @notif_wait: notification wait related data.
140d1e879ecSMiri Korenblit * @async_handlers_list: a list of all async RX handlers. When a notifciation
141d1e879ecSMiri Korenblit * with an async handler is received, it is added to this list.
142d1e879ecSMiri Korenblit * When &async_handlers_wk runs - it runs these handlers one by one.
143d1e879ecSMiri Korenblit * @async_handlers_lock: a lock for &async_handlers_list. Sync
144d1e879ecSMiri Korenblit * &async_handlers_wk and RX notifcation path.
145d1e879ecSMiri Korenblit * @async_handlers_wk: A work to run all async RX handlers from
146d1e879ecSMiri Korenblit * &async_handlers_list.
147d1e879ecSMiri Korenblit * @ct_kill_exit_wk: worker to exit thermal kill
148d1e879ecSMiri Korenblit * @fw_status: bitmap of fw status bits
149d1e879ecSMiri Korenblit * @running: true if the firmware is running
150d1e879ecSMiri Korenblit * @do_not_dump_once: true if firmware dump must be prevented once
151d1e879ecSMiri Korenblit * @in_d3: indicates FW is in suspend mode and should be resumed
152d1e879ecSMiri Korenblit * @in_hw_restart: indicates that we are currently in restart flow.
153d1e879ecSMiri Korenblit * rather than restarted. Should be unset upon restart.
154d1e879ecSMiri Korenblit * @radio_kill: bitmap of radio kill status
155d1e879ecSMiri Korenblit * @radio_kill.hw: radio is killed by hw switch
156d1e879ecSMiri Korenblit * @radio_kill.ct: radio is killed because the device it too hot
157d1e879ecSMiri Korenblit * @power_budget_mw: maximum cTDP power budget as defined for this system and
158d1e879ecSMiri Korenblit * device
159d1e879ecSMiri Korenblit * @addresses: device MAC addresses.
160d1e879ecSMiri Korenblit * @scan: instance of the scan object
161d1e879ecSMiri Korenblit * @wowlan: WoWLAN support data.
162d1e879ecSMiri Korenblit * @led: the led device
163d1e879ecSMiri Korenblit * @mcc_src: the source id of the MCC, comes from the firmware
164d1e879ecSMiri Korenblit * @bios_enable_puncturing: is puncturing enabled by bios
165d1e879ecSMiri Korenblit * @fw_id_to_ba: maps a fw (BA) id to a corresponding Block Ack session data.
166d1e879ecSMiri Korenblit * @num_rx_ba_sessions: tracks the number of active Rx Block Ack (BA) sessions.
167d1e879ecSMiri Korenblit * the driver ensures that new BA sessions are blocked once the maximum
168d1e879ecSMiri Korenblit * supported by the firmware is reached, preventing firmware asserts.
169d1e879ecSMiri Korenblit * @rxq_sync: manages RX queue sync state
170d1e879ecSMiri Korenblit * @txqs_to_add: a list of &ieee80211_txq's to allocate in &add_txqs_wk
171d1e879ecSMiri Korenblit * @add_txqs_wk: a worker to allocate txqs.
172d1e879ecSMiri Korenblit * @add_txqs_lock: to lock the &txqs_to_add list.
173d1e879ecSMiri Korenblit * @error_recovery_buf: pointer to the recovery buffer that will be read
174d1e879ecSMiri Korenblit * from firmware upon fw/hw error and sent back to the firmware in
175d1e879ecSMiri Korenblit * reconfig flow (after NIC reset).
176d1e879ecSMiri Korenblit * @mcast_filter_cmd: pointer to the multicast filter command.
177d1e879ecSMiri Korenblit * @mgmt_tx_ant: stores the last TX antenna index; used for setting
178d1e879ecSMiri Korenblit * TX rate_n_flags for non-STA mgmt frames (toggles on every TX failure).
179d1e879ecSMiri Korenblit * @fw_rates_ver_3: FW rates are in version 3
180d1e879ecSMiri Korenblit * @low_latency: low-latency manager.
181d1e879ecSMiri Korenblit * @tzone: thermal zone device's data
182d1e879ecSMiri Korenblit * @cooling_dev: cooling device's related data
183d1e879ecSMiri Korenblit * @ibss_manager: in IBSS mode (only one vif can be active), indicates what
184d1e879ecSMiri Korenblit * firmware indicated about having transmitted the last beacon, i.e.
185d1e879ecSMiri Korenblit * being IBSS manager for that time and needing to respond to probe
186d1e879ecSMiri Korenblit * requests
187d1e879ecSMiri Korenblit * @ptp_data: data of the PTP clock
188d1e879ecSMiri Korenblit * @time_sync: time sync data.
189d1e879ecSMiri Korenblit * @ftm_initiator: FTM initiator data
190d1e879ecSMiri Korenblit * @last_bt_notif: last received BT Coex notif
191d1e879ecSMiri Korenblit */
192d1e879ecSMiri Korenblit struct iwl_mld {
193d1e879ecSMiri Korenblit /* Add here fields that need clean up on restart */
194d1e879ecSMiri Korenblit struct_group(zeroed_on_hw_restart,
195d1e879ecSMiri Korenblit struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINK_ID + 1];
196d1e879ecSMiri Korenblit struct ieee80211_vif __rcu *fw_id_to_vif[NUM_MAC_INDEX_DRIVER];
197d1e879ecSMiri Korenblit struct ieee80211_txq __rcu *fw_id_to_txq[IWL_MAX_TVQM_QUEUES];
198d1e879ecSMiri Korenblit u8 used_phy_ids: NUM_PHY_CTX;
199d1e879ecSMiri Korenblit u8 num_igtks;
200d1e879ecSMiri Korenblit struct {
201d1e879ecSMiri Korenblit bool on;
202d1e879ecSMiri Korenblit u32 ampdu_ref;
203d1e879ecSMiri Korenblit bool ampdu_toggle;
204d1e879ecSMiri Korenblit u8 p80;
205d1e879ecSMiri Korenblit #ifdef CONFIG_IWLWIFI_DEBUGFS
206d1e879ecSMiri Korenblit __le16 cur_aid;
207d1e879ecSMiri Korenblit u8 cur_bssid[ETH_ALEN];
208d1e879ecSMiri Korenblit bool ptp_time;
209d1e879ecSMiri Korenblit #endif
210d1e879ecSMiri Korenblit } monitor;
211d1e879ecSMiri Korenblit #ifdef CONFIG_PM_SLEEP
212d1e879ecSMiri Korenblit bool netdetect;
213d1e879ecSMiri Korenblit #endif /* CONFIG_PM_SLEEP */
214d1e879ecSMiri Korenblit struct ieee80211_vif *p2p_device_vif;
215d1e879ecSMiri Korenblit struct iwl_bt_coex_profile_notif last_bt_notif;
216d1e879ecSMiri Korenblit );
217d1e879ecSMiri Korenblit struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_STATION_COUNT_MAX];
218d1e879ecSMiri Korenblit /* And here fields that survive a fw restart */
219d1e879ecSMiri Korenblit struct device *dev;
220d1e879ecSMiri Korenblit struct iwl_trans *trans;
221d1e879ecSMiri Korenblit const struct iwl_rf_cfg *cfg;
222d1e879ecSMiri Korenblit const struct iwl_fw *fw;
223d1e879ecSMiri Korenblit struct ieee80211_hw *hw;
224d1e879ecSMiri Korenblit struct wiphy *wiphy;
225d1e879ecSMiri Korenblit struct iwl_nvm_data *nvm_data;
226d1e879ecSMiri Korenblit struct iwl_fw_runtime fwrt;
227d1e879ecSMiri Korenblit struct dentry *debugfs_dir;
228d1e879ecSMiri Korenblit struct iwl_notif_wait_data notif_wait;
229d1e879ecSMiri Korenblit struct list_head async_handlers_list;
230d1e879ecSMiri Korenblit spinlock_t async_handlers_lock;
231d1e879ecSMiri Korenblit struct wiphy_work async_handlers_wk;
232d1e879ecSMiri Korenblit struct wiphy_delayed_work ct_kill_exit_wk;
233d1e879ecSMiri Korenblit
234d1e879ecSMiri Korenblit struct {
235d1e879ecSMiri Korenblit u32 running:1,
236d1e879ecSMiri Korenblit do_not_dump_once:1,
237d1e879ecSMiri Korenblit #ifdef CONFIG_PM_SLEEP
238d1e879ecSMiri Korenblit in_d3:1,
239d1e879ecSMiri Korenblit #endif
240d1e879ecSMiri Korenblit in_hw_restart:1;
241d1e879ecSMiri Korenblit
242d1e879ecSMiri Korenblit } fw_status;
243d1e879ecSMiri Korenblit
244d1e879ecSMiri Korenblit struct {
245d1e879ecSMiri Korenblit u32 hw:1,
246d1e879ecSMiri Korenblit ct:1;
247d1e879ecSMiri Korenblit } radio_kill;
248d1e879ecSMiri Korenblit
249d1e879ecSMiri Korenblit u32 power_budget_mw;
250d1e879ecSMiri Korenblit
251d1e879ecSMiri Korenblit struct mac_address addresses[IWL_MLD_MAX_ADDRESSES];
252d1e879ecSMiri Korenblit struct iwl_mld_scan scan;
253d1e879ecSMiri Korenblit #ifdef CONFIG_PM_SLEEP
254d1e879ecSMiri Korenblit struct wiphy_wowlan_support wowlan;
255d1e879ecSMiri Korenblit #endif /* CONFIG_PM_SLEEP */
256d1e879ecSMiri Korenblit #ifdef CONFIG_IWLWIFI_LEDS
257d1e879ecSMiri Korenblit struct led_classdev led;
258d1e879ecSMiri Korenblit #endif
259d1e879ecSMiri Korenblit enum iwl_mcc_source mcc_src;
260d1e879ecSMiri Korenblit bool bios_enable_puncturing;
261d1e879ecSMiri Korenblit
262d1e879ecSMiri Korenblit struct iwl_mld_baid_data __rcu *fw_id_to_ba[IWL_MAX_BAID];
263d1e879ecSMiri Korenblit u8 num_rx_ba_sessions;
264d1e879ecSMiri Korenblit
265d1e879ecSMiri Korenblit struct iwl_mld_rx_queues_sync rxq_sync;
266d1e879ecSMiri Korenblit
267d1e879ecSMiri Korenblit struct list_head txqs_to_add;
268d1e879ecSMiri Korenblit struct wiphy_work add_txqs_wk;
269d1e879ecSMiri Korenblit spinlock_t add_txqs_lock;
270d1e879ecSMiri Korenblit
271d1e879ecSMiri Korenblit u8 *error_recovery_buf;
272d1e879ecSMiri Korenblit struct iwl_mcast_filter_cmd *mcast_filter_cmd;
273d1e879ecSMiri Korenblit
274d1e879ecSMiri Korenblit u8 mgmt_tx_ant;
275d1e879ecSMiri Korenblit
276d1e879ecSMiri Korenblit bool fw_rates_ver_3;
277d1e879ecSMiri Korenblit
278d1e879ecSMiri Korenblit struct iwl_mld_low_latency low_latency;
279d1e879ecSMiri Korenblit
280d1e879ecSMiri Korenblit bool ibss_manager;
281*32c33a4dSAvraham Stern #ifdef CONFIG_THERMAL
282d1e879ecSMiri Korenblit struct thermal_zone_device *tzone;
283d1e879ecSMiri Korenblit struct iwl_mld_cooling_device cooling_dev;
284d1e879ecSMiri Korenblit #endif
285d1e879ecSMiri Korenblit
286d1e879ecSMiri Korenblit struct ptp_data ptp_data;
287d1e879ecSMiri Korenblit
288d1e879ecSMiri Korenblit struct iwl_mld_time_sync_data __rcu *time_sync;
289d1e879ecSMiri Korenblit
290d1e879ecSMiri Korenblit struct ftm_initiator_data ftm_initiator;
291d1e879ecSMiri Korenblit };
292d1e879ecSMiri Korenblit
293d1e879ecSMiri Korenblit /* memset the part of the struct that requires cleanup on restart */
294d1e879ecSMiri Korenblit #define CLEANUP_STRUCT(_ptr) \
295d1e879ecSMiri Korenblit memset((void *)&(_ptr)->zeroed_on_hw_restart, 0, \
296834bfc69SMiri Korenblit sizeof((_ptr)->zeroed_on_hw_restart))
297d1e879ecSMiri Korenblit
298834bfc69SMiri Korenblit /* Cleanup function for struct iwl_mld, will be called in restart */
299d1e879ecSMiri Korenblit static inline void
iwl_cleanup_mld(struct iwl_mld * mld)300d1e879ecSMiri Korenblit iwl_cleanup_mld(struct iwl_mld *mld)
301d1e879ecSMiri Korenblit {
302d1e879ecSMiri Korenblit CLEANUP_STRUCT(mld);
303d1e879ecSMiri Korenblit CLEANUP_STRUCT(&mld->scan);
304d1e879ecSMiri Korenblit
305d1e879ecSMiri Korenblit #ifdef CONFIG_PM_SLEEP
306d1e879ecSMiri Korenblit mld->fw_status.in_d3 = false;
307d1e879ecSMiri Korenblit #endif
308d1e879ecSMiri Korenblit
309d1e879ecSMiri Korenblit iwl_mld_low_latency_restart_cleanup(mld);
310d1e879ecSMiri Korenblit }
311d1e879ecSMiri Korenblit
312d1e879ecSMiri Korenblit enum iwl_power_scheme {
313d1e879ecSMiri Korenblit IWL_POWER_SCHEME_CAM = 1,
314d1e879ecSMiri Korenblit IWL_POWER_SCHEME_BPS,
315d1e879ecSMiri Korenblit };
316d1e879ecSMiri Korenblit
317d1e879ecSMiri Korenblit /**
318d1e879ecSMiri Korenblit * struct iwl_mld_mod_params - module parameters for iwlmld
319d1e879ecSMiri Korenblit * @power_scheme: one of enum iwl_power_scheme
320d1e879ecSMiri Korenblit */
321d1e879ecSMiri Korenblit struct iwl_mld_mod_params {
322d1e879ecSMiri Korenblit int power_scheme;
323d1e879ecSMiri Korenblit };
324d1e879ecSMiri Korenblit
325d1e879ecSMiri Korenblit extern struct iwl_mld_mod_params iwlmld_mod_params;
326d1e879ecSMiri Korenblit
327d1e879ecSMiri Korenblit /* Extract MLD priv from op_mode */
328d1e879ecSMiri Korenblit #define IWL_OP_MODE_GET_MLD(_iwl_op_mode) \
329d1e879ecSMiri Korenblit ((struct iwl_mld *)(_iwl_op_mode)->op_mode_specific)
330d1e879ecSMiri Korenblit
331d1e879ecSMiri Korenblit #define IWL_MAC80211_GET_MLD(_hw) \
332d1e879ecSMiri Korenblit IWL_OP_MODE_GET_MLD((struct iwl_op_mode *)((_hw)->priv))
333d1e879ecSMiri Korenblit
334d1e879ecSMiri Korenblit #ifdef CONFIG_IWLWIFI_DEBUGFS
335d1e879ecSMiri Korenblit void
336d1e879ecSMiri Korenblit iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir);
337d1e879ecSMiri Korenblit #else
338d1e879ecSMiri Korenblit static inline void
iwl_mld_add_debugfs_files(struct iwl_mld * mld,struct dentry * debugfs_dir)339d1e879ecSMiri Korenblit iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir)
340d1e879ecSMiri Korenblit {}
341d1e879ecSMiri Korenblit #endif
342d1e879ecSMiri Korenblit
343d1e879ecSMiri Korenblit int iwl_mld_load_fw(struct iwl_mld *mld);
344d1e879ecSMiri Korenblit void iwl_mld_stop_fw(struct iwl_mld *mld);
345d1e879ecSMiri Korenblit int iwl_mld_start_fw(struct iwl_mld *mld);
346d1e879ecSMiri Korenblit void iwl_mld_send_recovery_cmd(struct iwl_mld *mld, u32 flags);
347d1e879ecSMiri Korenblit
iwl_mld_set_ctkill(struct iwl_mld * mld,bool state)348d1e879ecSMiri Korenblit static inline void iwl_mld_set_ctkill(struct iwl_mld *mld, bool state)
349d1e879ecSMiri Korenblit {
350d1e879ecSMiri Korenblit mld->radio_kill.ct = state;
351d1e879ecSMiri Korenblit
352d1e879ecSMiri Korenblit wiphy_rfkill_set_hw_state(mld->wiphy,
353d1e879ecSMiri Korenblit mld->radio_kill.hw || mld->radio_kill.ct);
354d1e879ecSMiri Korenblit }
355d1e879ecSMiri Korenblit
iwl_mld_set_hwkill(struct iwl_mld * mld,bool state)356d1e879ecSMiri Korenblit static inline void iwl_mld_set_hwkill(struct iwl_mld *mld, bool state)
357d1e879ecSMiri Korenblit {
358d1e879ecSMiri Korenblit mld->radio_kill.hw = state;
359d1e879ecSMiri Korenblit
360d1e879ecSMiri Korenblit wiphy_rfkill_set_hw_state(mld->wiphy,
361d1e879ecSMiri Korenblit mld->radio_kill.hw || mld->radio_kill.ct);
362d1e879ecSMiri Korenblit }
363d1e879ecSMiri Korenblit
iwl_mld_get_valid_tx_ant(const struct iwl_mld * mld)364d1e879ecSMiri Korenblit static inline u8 iwl_mld_get_valid_tx_ant(const struct iwl_mld *mld)
365d1e879ecSMiri Korenblit {
366d1e879ecSMiri Korenblit u8 tx_ant = mld->fw->valid_tx_ant;
367d1e879ecSMiri Korenblit
368d1e879ecSMiri Korenblit if (mld->nvm_data && mld->nvm_data->valid_tx_ant)
369d1e879ecSMiri Korenblit tx_ant &= mld->nvm_data->valid_tx_ant;
370d1e879ecSMiri Korenblit
371d1e879ecSMiri Korenblit return tx_ant;
372d1e879ecSMiri Korenblit }
373d1e879ecSMiri Korenblit
iwl_mld_get_valid_rx_ant(const struct iwl_mld * mld)374d1e879ecSMiri Korenblit static inline u8 iwl_mld_get_valid_rx_ant(const struct iwl_mld *mld)
375d1e879ecSMiri Korenblit {
376d1e879ecSMiri Korenblit u8 rx_ant = mld->fw->valid_rx_ant;
377d1e879ecSMiri Korenblit
378d1e879ecSMiri Korenblit if (mld->nvm_data && mld->nvm_data->valid_rx_ant)
379d1e879ecSMiri Korenblit rx_ant &= mld->nvm_data->valid_rx_ant;
380d1e879ecSMiri Korenblit
381d1e879ecSMiri Korenblit return rx_ant;
382d1e879ecSMiri Korenblit }
383d1e879ecSMiri Korenblit
iwl_mld_nl80211_band_to_fw(enum nl80211_band band)384d1e879ecSMiri Korenblit static inline u8 iwl_mld_nl80211_band_to_fw(enum nl80211_band band)
385d1e879ecSMiri Korenblit {
386d1e879ecSMiri Korenblit switch (band) {
387d1e879ecSMiri Korenblit case NL80211_BAND_2GHZ:
388d1e879ecSMiri Korenblit return PHY_BAND_24;
389d1e879ecSMiri Korenblit case NL80211_BAND_5GHZ:
390d1e879ecSMiri Korenblit return PHY_BAND_5;
391d1e879ecSMiri Korenblit case NL80211_BAND_6GHZ:
392d1e879ecSMiri Korenblit return PHY_BAND_6;
393d1e879ecSMiri Korenblit default:
394d1e879ecSMiri Korenblit WARN_ONCE(1, "Unsupported band (%u)\n", band);
395d1e879ecSMiri Korenblit return PHY_BAND_5;
396d1e879ecSMiri Korenblit }
397d1e879ecSMiri Korenblit }
398d1e879ecSMiri Korenblit
iwl_mld_phy_band_to_nl80211(u8 phy_band)399d1e879ecSMiri Korenblit static inline u8 iwl_mld_phy_band_to_nl80211(u8 phy_band)
400d1e879ecSMiri Korenblit {
401d1e879ecSMiri Korenblit switch (phy_band) {
402d1e879ecSMiri Korenblit case PHY_BAND_24:
403d1e879ecSMiri Korenblit return NL80211_BAND_2GHZ;
404d1e879ecSMiri Korenblit case PHY_BAND_5:
405d1e879ecSMiri Korenblit return NL80211_BAND_5GHZ;
406d1e879ecSMiri Korenblit case PHY_BAND_6:
407d1e879ecSMiri Korenblit return NL80211_BAND_6GHZ;
408d1e879ecSMiri Korenblit default:
409d1e879ecSMiri Korenblit WARN_ONCE(1, "Unsupported phy band (%u)\n", phy_band);
410d1e879ecSMiri Korenblit return NL80211_BAND_5GHZ;
411d1e879ecSMiri Korenblit }
412d1e879ecSMiri Korenblit }
413d1e879ecSMiri Korenblit
414d1e879ecSMiri Korenblit static inline int
iwl_mld_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags,enum nl80211_band band)415d1e879ecSMiri Korenblit iwl_mld_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags,
416d1e879ecSMiri Korenblit enum nl80211_band band)
417d1e879ecSMiri Korenblit {
418d1e879ecSMiri Korenblit int format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
419d1e879ecSMiri Korenblit int rate = rate_n_flags & RATE_LEGACY_RATE_MSK;
420d1e879ecSMiri Korenblit bool is_lb = band == NL80211_BAND_2GHZ;
421d1e879ecSMiri Korenblit
422d1e879ecSMiri Korenblit if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM)
423d1e879ecSMiri Korenblit return is_lb ? rate + IWL_FIRST_OFDM_RATE : rate;
424d1e879ecSMiri Korenblit
425d1e879ecSMiri Korenblit /* CCK is not allowed in 5 GHz */
426d1e879ecSMiri Korenblit return is_lb ? rate : -1;
427d1e879ecSMiri Korenblit }
428d1e879ecSMiri Korenblit
429d1e879ecSMiri Korenblit extern const struct ieee80211_ops iwl_mld_hw_ops;
430d1e879ecSMiri Korenblit
431d1e879ecSMiri Korenblit /**
432d1e879ecSMiri Korenblit * enum iwl_rx_handler_context: context for Rx handler
433d1e879ecSMiri Korenblit * @RX_HANDLER_SYNC: this means that it will be called in the Rx path
434d1e879ecSMiri Korenblit * which can't acquire the wiphy->mutex.
435d1e879ecSMiri Korenblit * @RX_HANDLER_ASYNC: If the handler needs to hold wiphy->mutex
436d1e879ecSMiri Korenblit * (and only in this case!), it should be set as ASYNC. In that case,
437d1e879ecSMiri Korenblit * it will be called from a worker with wiphy->mutex held.
438d1e879ecSMiri Korenblit */
439d1e879ecSMiri Korenblit enum iwl_rx_handler_context {
440d1e879ecSMiri Korenblit RX_HANDLER_SYNC,
441d1e879ecSMiri Korenblit RX_HANDLER_ASYNC,
442d1e879ecSMiri Korenblit };
443d1e879ecSMiri Korenblit
444d1e879ecSMiri Korenblit /**
445d1e879ecSMiri Korenblit * struct iwl_rx_handler: handler for FW notification
446d1e879ecSMiri Korenblit * @val_fn: input validation function.
447d1e879ecSMiri Korenblit * @sizes: an array that mapps a version to the expected size.
448d1e879ecSMiri Korenblit * @fn: the function is called when notification is handled
449d1e879ecSMiri Korenblit * @cmd_id: command id
450d1e879ecSMiri Korenblit * @n_sizes: number of elements in &sizes.
451d1e879ecSMiri Korenblit * @context: see &iwl_rx_handler_context
452d1e879ecSMiri Korenblit * @obj_type: the type of the object that this handler is related to.
453d1e879ecSMiri Korenblit * See &iwl_mld_object_type. Use IWL_MLD_OBJECT_TYPE_NONE if not related.
454d1e879ecSMiri Korenblit * @cancel: function to cancel the notification. valid only if obj_type is not
455d1e879ecSMiri Korenblit * IWL_MLD_OBJECT_TYPE_NONE.
456d1e879ecSMiri Korenblit */
457d1e879ecSMiri Korenblit struct iwl_rx_handler {
458d1e879ecSMiri Korenblit union {
459d1e879ecSMiri Korenblit bool (*val_fn)(struct iwl_mld *mld, struct iwl_rx_packet *pkt);
460d1e879ecSMiri Korenblit const struct iwl_notif_struct_size *sizes;
461d1e879ecSMiri Korenblit };
462d1e879ecSMiri Korenblit void (*fn)(struct iwl_mld *mld, struct iwl_rx_packet *pkt);
463d1e879ecSMiri Korenblit u16 cmd_id;
464d1e879ecSMiri Korenblit u8 n_sizes;
465d1e879ecSMiri Korenblit u8 context;
466d1e879ecSMiri Korenblit enum iwl_mld_object_type obj_type;
467d1e879ecSMiri Korenblit bool (*cancel)(struct iwl_mld *mld, struct iwl_rx_packet *pkt,
468d1e879ecSMiri Korenblit u32 obj_id);
469d1e879ecSMiri Korenblit };
470d1e879ecSMiri Korenblit
471d1e879ecSMiri Korenblit /**
472d1e879ecSMiri Korenblit * struct iwl_notif_struct_size: map a notif ver to the expected size
473d1e879ecSMiri Korenblit *
474d1e879ecSMiri Korenblit * @size: the size to expect
475d1e879ecSMiri Korenblit * @ver: the version of the notification
476d1e879ecSMiri Korenblit */
477d1e879ecSMiri Korenblit struct iwl_notif_struct_size {
478d1e879ecSMiri Korenblit u32 size:24, ver:8;
479d1e879ecSMiri Korenblit };
480d1e879ecSMiri Korenblit
481d1e879ecSMiri Korenblit #if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
482d1e879ecSMiri Korenblit extern const struct iwl_hcmd_arr iwl_mld_groups[];
483d1e879ecSMiri Korenblit extern const unsigned int global_iwl_mld_goups_size;
484d1e879ecSMiri Korenblit extern const struct iwl_rx_handler iwl_mld_rx_handlers[];
485d1e879ecSMiri Korenblit extern const unsigned int iwl_mld_rx_handlers_num;
486d1e879ecSMiri Korenblit
487d1e879ecSMiri Korenblit bool
488d1e879ecSMiri Korenblit iwl_mld_is_dup(struct iwl_mld *mld, struct ieee80211_sta *sta,
489d1e879ecSMiri Korenblit struct ieee80211_hdr *hdr,
490d1e879ecSMiri Korenblit const struct iwl_rx_mpdu_desc *mpdu_desc,
491d1e879ecSMiri Korenblit struct ieee80211_rx_status *rx_status, int queue);
492d1e879ecSMiri Korenblit
493d1e879ecSMiri Korenblit void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans,
494d1e879ecSMiri Korenblit const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw,
495d1e879ecSMiri Korenblit struct ieee80211_hw *hw, struct dentry *dbgfs_dir);
496d1e879ecSMiri Korenblit #endif
497d1e879ecSMiri Korenblit
498d1e879ecSMiri Korenblit #define IWL_MLD_INVALID_FW_ID 0xff
499d1e879ecSMiri Korenblit
500d1e879ecSMiri Korenblit #define IWL_MLD_ALLOC_FN(_type, _mac80211_type) \
501d1e879ecSMiri Korenblit static int \
502d1e879ecSMiri Korenblit iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld, \
503d1e879ecSMiri Korenblit u8 *fw_id, \
504d1e879ecSMiri Korenblit struct ieee80211_##_mac80211_type *mac80211_ptr) \
505d1e879ecSMiri Korenblit { \
506d1e879ecSMiri Korenblit u8 rand = IWL_MLD_DIS_RANDOM_FW_ID ? 0 : get_random_u8(); \
507d1e879ecSMiri Korenblit u8 arr_sz = ARRAY_SIZE(mld->fw_id_to_##_mac80211_type); \
508d1e879ecSMiri Korenblit if (__builtin_types_compatible_p(typeof(*mac80211_ptr), \
509d1e879ecSMiri Korenblit struct ieee80211_link_sta)) \
510d1e879ecSMiri Korenblit arr_sz = mld->fw->ucode_capa.num_stations; \
511d1e879ecSMiri Korenblit if (__builtin_types_compatible_p(typeof(*mac80211_ptr), \
512d1e879ecSMiri Korenblit struct ieee80211_bss_conf)) \
513d1e879ecSMiri Korenblit arr_sz = mld->fw->ucode_capa.num_links; \
514d1e879ecSMiri Korenblit for (int i = 0; i < arr_sz; i++) { \
515d1e879ecSMiri Korenblit u8 idx = (i + rand) % arr_sz; \
516d1e879ecSMiri Korenblit if (rcu_access_pointer(mld->fw_id_to_##_mac80211_type[idx])) \
517d1e879ecSMiri Korenblit continue; \
518d1e879ecSMiri Korenblit IWL_DEBUG_INFO(mld, "Allocated at index %d / %d\n", idx, arr_sz); \
519d1e879ecSMiri Korenblit *fw_id = idx; \
520d1e879ecSMiri Korenblit rcu_assign_pointer(mld->fw_id_to_##_mac80211_type[idx], mac80211_ptr); \
521d1e879ecSMiri Korenblit return 0; \
522d1e879ecSMiri Korenblit } \
523d1e879ecSMiri Korenblit return -ENOSPC; \
524d1e879ecSMiri Korenblit }
525d1e879ecSMiri Korenblit
526d1e879ecSMiri Korenblit static inline struct ieee80211_bss_conf *
iwl_mld_fw_id_to_link_conf(struct iwl_mld * mld,u8 fw_link_id)527d1e879ecSMiri Korenblit iwl_mld_fw_id_to_link_conf(struct iwl_mld *mld, u8 fw_link_id)
528d1e879ecSMiri Korenblit {
529d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, fw_link_id >= mld->fw->ucode_capa.num_links,
530d1e879ecSMiri Korenblit "Invalid fw_link_id: %d\n", fw_link_id))
531d1e879ecSMiri Korenblit return NULL;
532d1e879ecSMiri Korenblit
533d1e879ecSMiri Korenblit return wiphy_dereference(mld->wiphy,
534d1e879ecSMiri Korenblit mld->fw_id_to_bss_conf[fw_link_id]);
535d1e879ecSMiri Korenblit }
536d1e879ecSMiri Korenblit
537d1e879ecSMiri Korenblit #define MSEC_TO_TU(_msec) ((_msec) * 1000 / 1024)
538d1e879ecSMiri Korenblit
539d1e879ecSMiri Korenblit void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw,
540d1e879ecSMiri Korenblit struct ieee80211_vif *vif);
541d1e879ecSMiri Korenblit void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw,
542d1e879ecSMiri Korenblit struct ieee80211_vif *vif,
543d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf,
544d1e879ecSMiri Korenblit struct dentry *dir);
545d1e879ecSMiri Korenblit void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw,
546d1e879ecSMiri Korenblit struct ieee80211_vif *vif,
547d1e879ecSMiri Korenblit struct ieee80211_link_sta *link_sta,
548d1e879ecSMiri Korenblit struct dentry *dir);
549d1e879ecSMiri Korenblit
550d1e879ecSMiri Korenblit /* Utilities */
551d1e879ecSMiri Korenblit
iwl_mld_mac80211_ac_to_fw_tx_fifo(enum ieee80211_ac_numbers ac)552d1e879ecSMiri Korenblit static inline u8 iwl_mld_mac80211_ac_to_fw_tx_fifo(enum ieee80211_ac_numbers ac)
553d1e879ecSMiri Korenblit {
554d1e879ecSMiri Korenblit static const u8 mac80211_ac_to_fw_tx_fifo[] = {
555d1e879ecSMiri Korenblit IWL_BZ_EDCA_TX_FIFO_VO,
556d1e879ecSMiri Korenblit IWL_BZ_EDCA_TX_FIFO_VI,
557d1e879ecSMiri Korenblit IWL_BZ_EDCA_TX_FIFO_BE,
558d1e879ecSMiri Korenblit IWL_BZ_EDCA_TX_FIFO_BK,
559d1e879ecSMiri Korenblit IWL_BZ_TRIG_TX_FIFO_VO,
560d1e879ecSMiri Korenblit IWL_BZ_TRIG_TX_FIFO_VI,
561d1e879ecSMiri Korenblit IWL_BZ_TRIG_TX_FIFO_BE,
562d1e879ecSMiri Korenblit IWL_BZ_TRIG_TX_FIFO_BK,
563d1e879ecSMiri Korenblit };
564d1e879ecSMiri Korenblit return mac80211_ac_to_fw_tx_fifo[ac];
565d1e879ecSMiri Korenblit }
566d1e879ecSMiri Korenblit
567d1e879ecSMiri Korenblit static inline u32
iwl_mld_get_lmac_id(struct iwl_mld * mld,enum nl80211_band band)568d1e879ecSMiri Korenblit iwl_mld_get_lmac_id(struct iwl_mld *mld, enum nl80211_band band)
569d1e879ecSMiri Korenblit {
570d1e879ecSMiri Korenblit if (!fw_has_capa(&mld->fw->ucode_capa,
571d1e879ecSMiri Korenblit IWL_UCODE_TLV_CAPA_CDB_SUPPORT) ||
572d1e879ecSMiri Korenblit band == NL80211_BAND_2GHZ)
573d1e879ecSMiri Korenblit return IWL_LMAC_24G_INDEX;
574d1e879ecSMiri Korenblit return IWL_LMAC_5G_INDEX;
575d1e879ecSMiri Korenblit }
576d1e879ecSMiri Korenblit
577d1e879ecSMiri Korenblit /* Check if we had an error, but reconfig flow didn't start yet */
iwl_mld_error_before_recovery(struct iwl_mld * mld)578 static inline bool iwl_mld_error_before_recovery(struct iwl_mld *mld)
579 {
580 return mld->fw_status.in_hw_restart &&
581 !iwl_trans_fw_running(mld->trans);
582 }
583
584 int iwl_mld_tdls_sta_count(struct iwl_mld *mld);
585
586 #endif /* __iwl_mld_h__ */
587