summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/intel/iwlwifi/mld/sta.h
blob: ddcffd7b9fde1e7fc18e820d5440ab8f9dfe8108 (plain)
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
/* 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);

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,
			    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__ */