1*d1e879ecSMiri Korenblit /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ 2*d1e879ecSMiri Korenblit /* 3*d1e879ecSMiri Korenblit * Copyright (C) 2024-2025 Intel Corporation 4*d1e879ecSMiri Korenblit */ 5*d1e879ecSMiri Korenblit 6*d1e879ecSMiri Korenblit #ifndef __iwl_mld_sta_h__ 7*d1e879ecSMiri Korenblit #define __iwl_mld_sta_h__ 8*d1e879ecSMiri Korenblit 9*d1e879ecSMiri Korenblit #include <net/mac80211.h> 10*d1e879ecSMiri Korenblit 11*d1e879ecSMiri Korenblit #include "mld.h" 12*d1e879ecSMiri Korenblit #include "tx.h" 13*d1e879ecSMiri Korenblit 14*d1e879ecSMiri Korenblit /** 15*d1e879ecSMiri Korenblit * struct iwl_mld_rxq_dup_data - Duplication detection data, per STA & Rx queue 16*d1e879ecSMiri Korenblit * @last_seq: last sequence per tid. 17*d1e879ecSMiri Korenblit * @last_sub_frame_idx: the index of the last subframe in an A-MSDU. This value 18*d1e879ecSMiri Korenblit * will be zero if the packet is not part of an A-MSDU. 19*d1e879ecSMiri Korenblit */ 20*d1e879ecSMiri Korenblit struct iwl_mld_rxq_dup_data { 21*d1e879ecSMiri Korenblit __le16 last_seq[IWL_MAX_TID_COUNT + 1]; 22*d1e879ecSMiri Korenblit u8 last_sub_frame_idx[IWL_MAX_TID_COUNT + 1]; 23*d1e879ecSMiri Korenblit } ____cacheline_aligned_in_smp; 24*d1e879ecSMiri Korenblit 25*d1e879ecSMiri Korenblit /** 26*d1e879ecSMiri Korenblit * struct iwl_mld_link_sta - link-level station 27*d1e879ecSMiri Korenblit * 28*d1e879ecSMiri Korenblit * This represents the link-level sta - the driver level equivalent to the 29*d1e879ecSMiri Korenblit * ieee80211_link_sta 30*d1e879ecSMiri Korenblit * 31*d1e879ecSMiri Korenblit * @last_rate_n_flags: rate_n_flags from the last &iwl_tlc_update_notif 32*d1e879ecSMiri Korenblit * @signal_avg: the signal average coming from the firmware 33*d1e879ecSMiri Korenblit * @in_fw: whether the link STA is uploaded to the FW (false during restart) 34*d1e879ecSMiri Korenblit * @rcu_head: RCU head for freeing this object 35*d1e879ecSMiri Korenblit * @fw_id: the FW id of this link sta. 36*d1e879ecSMiri Korenblit */ 37*d1e879ecSMiri Korenblit struct iwl_mld_link_sta { 38*d1e879ecSMiri Korenblit /* Add here fields that need clean up on restart */ 39*d1e879ecSMiri Korenblit struct_group(zeroed_on_hw_restart, 40*d1e879ecSMiri Korenblit u32 last_rate_n_flags; 41*d1e879ecSMiri Korenblit bool in_fw; 42*d1e879ecSMiri Korenblit s8 signal_avg; 43*d1e879ecSMiri Korenblit ); 44*d1e879ecSMiri Korenblit /* And here fields that survive a fw restart */ 45*d1e879ecSMiri Korenblit struct rcu_head rcu_head; 46*d1e879ecSMiri Korenblit u32 fw_id; 47*d1e879ecSMiri Korenblit }; 48*d1e879ecSMiri Korenblit 49*d1e879ecSMiri Korenblit #define iwl_mld_link_sta_dereference_check(mld_sta, link_id) \ 50*d1e879ecSMiri Korenblit rcu_dereference_check((mld_sta)->link[link_id], \ 51*d1e879ecSMiri Korenblit lockdep_is_held(&mld_sta->mld->wiphy->mtx)) 52*d1e879ecSMiri Korenblit 53*d1e879ecSMiri Korenblit #define for_each_mld_link_sta(mld_sta, link_sta, link_id) \ 54*d1e879ecSMiri Korenblit for (link_id = 0; link_id < ARRAY_SIZE((mld_sta)->link); \ 55*d1e879ecSMiri Korenblit link_id++) \ 56*d1e879ecSMiri Korenblit if ((link_sta = \ 57*d1e879ecSMiri Korenblit iwl_mld_link_sta_dereference_check(mld_sta, link_id))) 58*d1e879ecSMiri Korenblit 59*d1e879ecSMiri Korenblit #define IWL_NUM_DEFAULT_KEYS 4 60*d1e879ecSMiri Korenblit 61*d1e879ecSMiri Korenblit /* struct iwl_mld_ptk_pn - Holds Packet Number (PN) per TID. 62*d1e879ecSMiri Korenblit * @rcu_head: RCU head for freeing this data. 63*d1e879ecSMiri Korenblit * @pn: Array storing PN for each TID. 64*d1e879ecSMiri Korenblit */ 65*d1e879ecSMiri Korenblit struct iwl_mld_ptk_pn { 66*d1e879ecSMiri Korenblit struct rcu_head rcu_head; 67*d1e879ecSMiri Korenblit struct { 68*d1e879ecSMiri Korenblit u8 pn[IWL_MAX_TID_COUNT][IEEE80211_CCMP_PN_LEN]; 69*d1e879ecSMiri Korenblit } ____cacheline_aligned_in_smp q[]; 70*d1e879ecSMiri Korenblit }; 71*d1e879ecSMiri Korenblit 72*d1e879ecSMiri Korenblit /** 73*d1e879ecSMiri Korenblit * struct iwl_mld_per_link_mpdu_counter - per-link TX/RX MPDU counters 74*d1e879ecSMiri Korenblit * 75*d1e879ecSMiri Korenblit * @tx: Number of TX MPDUs. 76*d1e879ecSMiri Korenblit * @rx: Number of RX MPDUs. 77*d1e879ecSMiri Korenblit */ 78*d1e879ecSMiri Korenblit struct iwl_mld_per_link_mpdu_counter { 79*d1e879ecSMiri Korenblit u32 tx; 80*d1e879ecSMiri Korenblit u32 rx; 81*d1e879ecSMiri Korenblit }; 82*d1e879ecSMiri Korenblit 83*d1e879ecSMiri Korenblit /** 84*d1e879ecSMiri Korenblit * struct iwl_mld_per_q_mpdu_counter - per-queue MPDU counter 85*d1e879ecSMiri Korenblit * 86*d1e879ecSMiri Korenblit * @lock: Needed to protect the counters when modified from statistics. 87*d1e879ecSMiri Korenblit * @per_link: per-link counters. 88*d1e879ecSMiri Korenblit * @window_start_time: timestamp of the counting-window start 89*d1e879ecSMiri Korenblit */ 90*d1e879ecSMiri Korenblit struct iwl_mld_per_q_mpdu_counter { 91*d1e879ecSMiri Korenblit spinlock_t lock; 92*d1e879ecSMiri Korenblit struct iwl_mld_per_link_mpdu_counter per_link[IWL_FW_MAX_LINK_ID + 1]; 93*d1e879ecSMiri Korenblit unsigned long window_start_time; 94*d1e879ecSMiri Korenblit } ____cacheline_aligned_in_smp; 95*d1e879ecSMiri Korenblit 96*d1e879ecSMiri Korenblit /** 97*d1e879ecSMiri Korenblit * struct iwl_mld_sta - representation of a station in the driver. 98*d1e879ecSMiri Korenblit * 99*d1e879ecSMiri Korenblit * This represent the MLD-level sta, and will not be added to the FW. 100*d1e879ecSMiri Korenblit * Embedded in ieee80211_sta. 101*d1e879ecSMiri Korenblit * 102*d1e879ecSMiri Korenblit * @vif: pointer the vif object. 103*d1e879ecSMiri Korenblit * @sta_state: station state according to enum %ieee80211_sta_state 104*d1e879ecSMiri Korenblit * @sta_type: type of this station. See &enum iwl_fw_sta_type 105*d1e879ecSMiri Korenblit * @mld: a pointer to the iwl_mld object 106*d1e879ecSMiri Korenblit * @dup_data: per queue duplicate packet detection data 107*d1e879ecSMiri Korenblit * @data_tx_ant: stores the last TX antenna index; used for setting 108*d1e879ecSMiri Korenblit * TX rate_n_flags for injected data frames (toggles on every TX failure). 109*d1e879ecSMiri Korenblit * @tid_to_baid: a simple map of TID to Block-Ack fw id 110*d1e879ecSMiri Korenblit * @deflink: This holds the default link STA information, for non MLO STA all 111*d1e879ecSMiri Korenblit * link specific STA information is accessed through @deflink or through 112*d1e879ecSMiri Korenblit * link[0] which points to address of @deflink. For MLO Link STA 113*d1e879ecSMiri Korenblit * the first added link STA will point to deflink. 114*d1e879ecSMiri Korenblit * @link: reference to Link Sta entries. For Non MLO STA, except 1st link, 115*d1e879ecSMiri Korenblit * i.e link[0] all links would be assigned to NULL by default and 116*d1e879ecSMiri Korenblit * would access link information via @deflink or link[0]. For MLO 117*d1e879ecSMiri Korenblit * STA, first link STA being added will point its link pointer to 118*d1e879ecSMiri Korenblit * @deflink address and remaining would be allocated and the address 119*d1e879ecSMiri Korenblit * would be assigned to link[link_id] where link_id is the id assigned 120*d1e879ecSMiri Korenblit * by the AP. 121*d1e879ecSMiri Korenblit * @ptk_pn: Array of pointers to PTK PN data, used to track the Packet Number 122*d1e879ecSMiri Korenblit * per key index and per queue (TID). 123*d1e879ecSMiri Korenblit * @mpdu_counters: RX/TX MPDUs counters for each queue. 124*d1e879ecSMiri Korenblit */ 125*d1e879ecSMiri Korenblit struct iwl_mld_sta { 126*d1e879ecSMiri Korenblit /* Add here fields that need clean up on restart */ 127*d1e879ecSMiri Korenblit struct_group(zeroed_on_hw_restart, 128*d1e879ecSMiri Korenblit enum ieee80211_sta_state sta_state; 129*d1e879ecSMiri Korenblit enum iwl_fw_sta_type sta_type; 130*d1e879ecSMiri Korenblit ); 131*d1e879ecSMiri Korenblit /* And here fields that survive a fw restart */ 132*d1e879ecSMiri Korenblit struct iwl_mld *mld; 133*d1e879ecSMiri Korenblit struct ieee80211_vif *vif; 134*d1e879ecSMiri Korenblit struct iwl_mld_rxq_dup_data *dup_data; 135*d1e879ecSMiri Korenblit u8 tid_to_baid[IWL_MAX_TID_COUNT]; 136*d1e879ecSMiri Korenblit u8 data_tx_ant; 137*d1e879ecSMiri Korenblit 138*d1e879ecSMiri Korenblit struct iwl_mld_link_sta deflink; 139*d1e879ecSMiri Korenblit struct iwl_mld_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; 140*d1e879ecSMiri Korenblit struct iwl_mld_ptk_pn __rcu *ptk_pn[IWL_NUM_DEFAULT_KEYS]; 141*d1e879ecSMiri Korenblit struct iwl_mld_per_q_mpdu_counter *mpdu_counters; 142*d1e879ecSMiri Korenblit }; 143*d1e879ecSMiri Korenblit 144*d1e879ecSMiri Korenblit static inline struct iwl_mld_sta * 145*d1e879ecSMiri Korenblit iwl_mld_sta_from_mac80211(struct ieee80211_sta *sta) 146*d1e879ecSMiri Korenblit { 147*d1e879ecSMiri Korenblit return (void *)sta->drv_priv; 148*d1e879ecSMiri Korenblit } 149*d1e879ecSMiri Korenblit 150*d1e879ecSMiri Korenblit static inline void 151*d1e879ecSMiri Korenblit iwl_mld_cleanup_sta(void *data, struct ieee80211_sta *sta) 152*d1e879ecSMiri Korenblit { 153*d1e879ecSMiri Korenblit struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); 154*d1e879ecSMiri Korenblit struct iwl_mld_link_sta *mld_link_sta; 155*d1e879ecSMiri Korenblit u8 link_id; 156*d1e879ecSMiri Korenblit 157*d1e879ecSMiri Korenblit for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) 158*d1e879ecSMiri Korenblit CLEANUP_STRUCT(iwl_mld_txq_from_mac80211(sta->txq[i])); 159*d1e879ecSMiri Korenblit 160*d1e879ecSMiri Korenblit for_each_mld_link_sta(mld_sta, mld_link_sta, link_id) { 161*d1e879ecSMiri Korenblit CLEANUP_STRUCT(mld_link_sta); 162*d1e879ecSMiri Korenblit 163*d1e879ecSMiri Korenblit if (!ieee80211_vif_is_mld(mld_sta->vif)) { 164*d1e879ecSMiri Korenblit /* not an MLD STA; only has the deflink with ID zero */ 165*d1e879ecSMiri Korenblit WARN_ON(link_id); 166*d1e879ecSMiri Korenblit continue; 167*d1e879ecSMiri Korenblit } 168*d1e879ecSMiri Korenblit 169*d1e879ecSMiri Korenblit if (mld_sta->vif->active_links & BIT(link_id)) 170*d1e879ecSMiri Korenblit continue; 171*d1e879ecSMiri Korenblit 172*d1e879ecSMiri Korenblit /* Should not happen as link removal should always succeed */ 173*d1e879ecSMiri Korenblit WARN_ON(1); 174*d1e879ecSMiri Korenblit RCU_INIT_POINTER(mld_sta->link[link_id], NULL); 175*d1e879ecSMiri Korenblit RCU_INIT_POINTER(mld_sta->mld->fw_id_to_link_sta[mld_link_sta->fw_id], 176*d1e879ecSMiri Korenblit NULL); 177*d1e879ecSMiri Korenblit if (mld_link_sta != &mld_sta->deflink) 178*d1e879ecSMiri Korenblit kfree_rcu(mld_link_sta, rcu_head); 179*d1e879ecSMiri Korenblit } 180*d1e879ecSMiri Korenblit 181*d1e879ecSMiri Korenblit CLEANUP_STRUCT(mld_sta); 182*d1e879ecSMiri Korenblit } 183*d1e879ecSMiri Korenblit 184*d1e879ecSMiri Korenblit static inline struct iwl_mld_link_sta * 185*d1e879ecSMiri Korenblit iwl_mld_link_sta_from_mac80211(struct ieee80211_link_sta *link_sta) 186*d1e879ecSMiri Korenblit { 187*d1e879ecSMiri Korenblit struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); 188*d1e879ecSMiri Korenblit 189*d1e879ecSMiri Korenblit return iwl_mld_link_sta_dereference_check(mld_sta, link_sta->link_id); 190*d1e879ecSMiri Korenblit } 191*d1e879ecSMiri Korenblit 192*d1e879ecSMiri Korenblit int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta, 193*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, enum iwl_fw_sta_type type); 194*d1e879ecSMiri Korenblit void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta); 195*d1e879ecSMiri Korenblit int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld, 196*d1e879ecSMiri Korenblit struct ieee80211_link_sta *link_sta); 197*d1e879ecSMiri Korenblit u32 iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta); 198*d1e879ecSMiri Korenblit int iwl_mld_update_all_link_stations(struct iwl_mld *mld, 199*d1e879ecSMiri Korenblit struct ieee80211_sta *sta); 200*d1e879ecSMiri Korenblit void iwl_mld_flush_sta_txqs(struct iwl_mld *mld, struct ieee80211_sta *sta); 201*d1e879ecSMiri Korenblit void iwl_mld_wait_sta_txqs_empty(struct iwl_mld *mld, 202*d1e879ecSMiri Korenblit struct ieee80211_sta *sta); 203*d1e879ecSMiri Korenblit void iwl_mld_count_mpdu_rx(struct ieee80211_link_sta *link_sta, int queue, 204*d1e879ecSMiri Korenblit u32 count); 205*d1e879ecSMiri Korenblit void iwl_mld_count_mpdu_tx(struct ieee80211_link_sta *link_sta, u32 count); 206*d1e879ecSMiri Korenblit 207*d1e879ecSMiri Korenblit /** 208*d1e879ecSMiri Korenblit * struct iwl_mld_int_sta - representation of an internal station 209*d1e879ecSMiri Korenblit * (a station that exist in FW and in driver, but not in mac80211) 210*d1e879ecSMiri Korenblit * 211*d1e879ecSMiri Korenblit * @sta_id: the index of the station in the fw 212*d1e879ecSMiri Korenblit * @queue_id: the if of the queue used by the station 213*d1e879ecSMiri Korenblit * @sta_type: station type. One of &iwl_fw_sta_type 214*d1e879ecSMiri Korenblit */ 215*d1e879ecSMiri Korenblit struct iwl_mld_int_sta { 216*d1e879ecSMiri Korenblit u8 sta_id; 217*d1e879ecSMiri Korenblit u32 queue_id; 218*d1e879ecSMiri Korenblit enum iwl_fw_sta_type sta_type; 219*d1e879ecSMiri Korenblit }; 220*d1e879ecSMiri Korenblit 221*d1e879ecSMiri Korenblit static inline void 222*d1e879ecSMiri Korenblit iwl_mld_init_internal_sta(struct iwl_mld_int_sta *internal_sta) 223*d1e879ecSMiri Korenblit { 224*d1e879ecSMiri Korenblit internal_sta->sta_id = IWL_INVALID_STA; 225*d1e879ecSMiri Korenblit internal_sta->queue_id = IWL_MLD_INVALID_QUEUE; 226*d1e879ecSMiri Korenblit } 227*d1e879ecSMiri Korenblit 228*d1e879ecSMiri Korenblit static inline void 229*d1e879ecSMiri Korenblit iwl_mld_free_internal_sta(struct iwl_mld *mld, 230*d1e879ecSMiri Korenblit struct iwl_mld_int_sta *internal_sta) 231*d1e879ecSMiri Korenblit { 232*d1e879ecSMiri Korenblit if (WARN_ON(internal_sta->sta_id == IWL_INVALID_STA)) 233*d1e879ecSMiri Korenblit return; 234*d1e879ecSMiri Korenblit 235*d1e879ecSMiri Korenblit RCU_INIT_POINTER(mld->fw_id_to_link_sta[internal_sta->sta_id], NULL); 236*d1e879ecSMiri Korenblit iwl_mld_init_internal_sta(internal_sta); 237*d1e879ecSMiri Korenblit } 238*d1e879ecSMiri Korenblit 239*d1e879ecSMiri Korenblit int iwl_mld_add_bcast_sta(struct iwl_mld *mld, 240*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 241*d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link); 242*d1e879ecSMiri Korenblit 243*d1e879ecSMiri Korenblit int iwl_mld_add_mcast_sta(struct iwl_mld *mld, 244*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 245*d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link); 246*d1e879ecSMiri Korenblit 247*d1e879ecSMiri Korenblit int iwl_mld_add_aux_sta(struct iwl_mld *mld, 248*d1e879ecSMiri Korenblit struct iwl_mld_int_sta *internal_sta); 249*d1e879ecSMiri Korenblit 250*d1e879ecSMiri Korenblit void iwl_mld_remove_bcast_sta(struct iwl_mld *mld, 251*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 252*d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link); 253*d1e879ecSMiri Korenblit 254*d1e879ecSMiri Korenblit void iwl_mld_remove_mcast_sta(struct iwl_mld *mld, 255*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 256*d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link); 257*d1e879ecSMiri Korenblit 258*d1e879ecSMiri Korenblit void iwl_mld_remove_aux_sta(struct iwl_mld *mld, 259*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 260*d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link); 261*d1e879ecSMiri Korenblit 262*d1e879ecSMiri Korenblit int iwl_mld_update_link_stas(struct iwl_mld *mld, 263*d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 264*d1e879ecSMiri Korenblit struct ieee80211_sta *sta, 265*d1e879ecSMiri Korenblit u16 old_links, u16 new_links); 266*d1e879ecSMiri Korenblit #endif /* __iwl_mld_sta_h__ */ 267