Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/mvm/sf.c
48287 views
1
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2
/*
3
* Copyright (C) 2013-2014, 2018-2019, 2022-2024 Intel Corporation
4
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
5
*/
6
#include "mvm.h"
7
8
/* For counting bound interfaces */
9
struct iwl_mvm_active_iface_iterator_data {
10
struct ieee80211_vif *ignore_vif;
11
struct ieee80211_sta *sta_vif_ap_sta;
12
enum iwl_sf_state sta_vif_state;
13
u32 num_active_macs;
14
};
15
16
/*
17
* Count bound interfaces which are not p2p, besides data->ignore_vif.
18
* data->station_vif will point to one bound vif of type station, if exists.
19
*/
20
static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac,
21
struct ieee80211_vif *vif)
22
{
23
struct iwl_mvm_active_iface_iterator_data *data = _data;
24
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
25
26
if (vif == data->ignore_vif || !mvmvif->deflink.phy_ctxt ||
27
vif->type == NL80211_IFTYPE_P2P_DEVICE)
28
return;
29
30
data->num_active_macs++;
31
32
if (vif->type == NL80211_IFTYPE_STATION) {
33
data->sta_vif_ap_sta = mvmvif->ap_sta;
34
if (vif->cfg.assoc)
35
data->sta_vif_state = SF_FULL_ON;
36
else
37
data->sta_vif_state = SF_INIT_OFF;
38
}
39
}
40
41
/*
42
* Aging and idle timeouts for the different possible scenarios
43
* in default configuration
44
*/
45
static const
46
__le32 sf_full_timeout_def[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
47
{
48
cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER_DEF),
49
cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER_DEF)
50
},
51
{
52
cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER_DEF),
53
cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER_DEF)
54
},
55
{
56
cpu_to_le32(SF_MCAST_AGING_TIMER_DEF),
57
cpu_to_le32(SF_MCAST_IDLE_TIMER_DEF)
58
},
59
{
60
cpu_to_le32(SF_BA_AGING_TIMER_DEF),
61
cpu_to_le32(SF_BA_IDLE_TIMER_DEF)
62
},
63
{
64
cpu_to_le32(SF_TX_RE_AGING_TIMER_DEF),
65
cpu_to_le32(SF_TX_RE_IDLE_TIMER_DEF)
66
},
67
};
68
69
/*
70
* Aging and idle timeouts for the different possible scenarios
71
* in single BSS MAC configuration.
72
*/
73
static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
74
{
75
cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER),
76
cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER)
77
},
78
{
79
cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER),
80
cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER)
81
},
82
{
83
cpu_to_le32(SF_MCAST_AGING_TIMER),
84
cpu_to_le32(SF_MCAST_IDLE_TIMER)
85
},
86
{
87
cpu_to_le32(SF_BA_AGING_TIMER),
88
cpu_to_le32(SF_BA_IDLE_TIMER)
89
},
90
{
91
cpu_to_le32(SF_TX_RE_AGING_TIMER),
92
cpu_to_le32(SF_TX_RE_IDLE_TIMER)
93
},
94
};
95
96
static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm,
97
struct iwl_sf_cfg_cmd *sf_cmd,
98
struct ieee80211_sta *sta)
99
{
100
int i, j, watermark;
101
u8 max_rx_nss = 0;
102
bool is_legacy = true;
103
struct ieee80211_link_sta *link_sta;
104
unsigned int link_id;
105
106
sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN);
107
108
/*
109
* If we are in association flow - check antenna configuration
110
* capabilities of the AP station, and choose the watermark accordingly.
111
*/
112
if (sta) {
113
/* find the maximal NSS number among all links (if relevant) */
114
rcu_read_lock();
115
for (link_id = 0; link_id < ARRAY_SIZE(sta->link); link_id++) {
116
link_sta = rcu_dereference(sta->link[link_id]);
117
if (!link_sta)
118
continue;
119
120
if (link_sta->ht_cap.ht_supported ||
121
link_sta->vht_cap.vht_supported ||
122
link_sta->eht_cap.has_eht ||
123
link_sta->he_cap.has_he) {
124
is_legacy = false;
125
max_rx_nss = max(max_rx_nss, link_sta->rx_nss);
126
}
127
}
128
rcu_read_unlock();
129
130
if (!is_legacy) {
131
switch (max_rx_nss) {
132
case 1:
133
watermark = SF_W_MARK_SISO;
134
break;
135
case 2:
136
watermark = SF_W_MARK_MIMO2;
137
break;
138
default:
139
watermark = SF_W_MARK_MIMO3;
140
break;
141
}
142
} else {
143
watermark = SF_W_MARK_LEGACY;
144
}
145
/* default watermark value for unassociated mode. */
146
} else {
147
watermark = SF_W_MARK_MIMO2;
148
}
149
sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark);
150
151
for (i = 0; i < SF_NUM_SCENARIO; i++) {
152
for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) {
153
sf_cmd->long_delay_timeouts[i][j] =
154
cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);
155
}
156
}
157
158
if (sta) {
159
BUILD_BUG_ON(sizeof(sf_full_timeout) !=
160
sizeof(__le32) * SF_NUM_SCENARIO *
161
SF_NUM_TIMEOUT_TYPES);
162
163
memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
164
sizeof(sf_full_timeout));
165
} else {
166
BUILD_BUG_ON(sizeof(sf_full_timeout_def) !=
167
sizeof(__le32) * SF_NUM_SCENARIO *
168
SF_NUM_TIMEOUT_TYPES);
169
170
memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def,
171
sizeof(sf_full_timeout_def));
172
}
173
}
174
175
static int iwl_mvm_sf_config(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
176
enum iwl_sf_state new_state)
177
{
178
struct iwl_sf_cfg_cmd sf_cmd = {
179
.state = cpu_to_le32(new_state),
180
};
181
int ret = 0;
182
183
/*
184
* If an associated AP sta changed its antenna configuration, the state
185
* will remain FULL_ON but SF parameters need to be reconsidered.
186
*/
187
if (new_state != SF_FULL_ON && mvm->sf_state == new_state)
188
return 0;
189
190
switch (new_state) {
191
case SF_UNINIT:
192
iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);
193
break;
194
case SF_FULL_ON:
195
if (!sta) {
196
IWL_ERR(mvm,
197
"No station: Cannot switch SF to FULL_ON\n");
198
return -EINVAL;
199
}
200
iwl_mvm_fill_sf_command(mvm, &sf_cmd, sta);
201
break;
202
case SF_INIT_OFF:
203
iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);
204
break;
205
default:
206
WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",
207
new_state);
208
return -EINVAL;
209
}
210
211
ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC,
212
sizeof(sf_cmd), &sf_cmd);
213
if (!ret)
214
mvm->sf_state = new_state;
215
216
return ret;
217
}
218
219
/*
220
* Update Smart fifo:
221
* Count bound interfaces that are not to be removed, ignoring p2p devices,
222
* and set new state accordingly.
223
*/
224
int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,
225
bool remove_vif)
226
{
227
enum iwl_sf_state new_state;
228
struct iwl_mvm_vif *mvmvif = NULL;
229
struct iwl_mvm_active_iface_iterator_data data = {
230
.ignore_vif = changed_vif,
231
.sta_vif_state = SF_UNINIT,
232
};
233
struct ieee80211_sta *sta = NULL;
234
235
if (fw_has_api(&mvm->fw->ucode_capa,
236
IWL_UCODE_TLV_API_SMART_FIFO_OFFLOAD))
237
return 0;
238
/*
239
* Ignore the call if we are in HW Restart flow, or if the handled
240
* vif is a p2p device.
241
*/
242
if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||
243
(changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE))
244
return 0;
245
246
ieee80211_iterate_active_interfaces_atomic(mvm->hw,
247
IEEE80211_IFACE_ITER_NORMAL,
248
iwl_mvm_bound_iface_iterator,
249
&data);
250
251
/* If changed_vif exists and is not to be removed, add to the count */
252
if (changed_vif && !remove_vif)
253
data.num_active_macs++;
254
255
switch (data.num_active_macs) {
256
case 0:
257
/* If there are no active macs - change state to SF_INIT_OFF */
258
new_state = SF_INIT_OFF;
259
break;
260
case 1:
261
if (remove_vif) {
262
/* The one active mac left is of type station
263
* and we filled the relevant data during iteration
264
*/
265
new_state = data.sta_vif_state;
266
sta = data.sta_vif_ap_sta;
267
} else {
268
if (WARN_ON(!changed_vif))
269
return -EINVAL;
270
if (changed_vif->type != NL80211_IFTYPE_STATION) {
271
new_state = SF_UNINIT;
272
} else if (changed_vif->cfg.assoc &&
273
changed_vif->bss_conf.dtim_period) {
274
mvmvif = iwl_mvm_vif_from_mac80211(changed_vif);
275
sta = mvmvif->ap_sta;
276
new_state = SF_FULL_ON;
277
} else {
278
new_state = SF_INIT_OFF;
279
}
280
}
281
break;
282
default:
283
/* If there are multiple active macs - change to SF_UNINIT */
284
new_state = SF_UNINIT;
285
}
286
287
return iwl_mvm_sf_config(mvm, sta, new_state);
288
}
289
290