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