Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/mld/power.c
48285 views
1
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2
/*
3
* Copyright (C) 2024-2025 Intel Corporation
4
*/
5
#include <net/mac80211.h>
6
7
#include "mld.h"
8
#include "hcmd.h"
9
#include "power.h"
10
#include "iface.h"
11
#include "link.h"
12
#include "constants.h"
13
14
static void iwl_mld_vif_ps_iterator(void *data, u8 *mac,
15
struct ieee80211_vif *vif)
16
{
17
bool *ps_enable = (bool *)data;
18
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
19
20
if (vif->type != NL80211_IFTYPE_STATION)
21
return;
22
23
*ps_enable &= !mld_vif->ps_disabled;
24
}
25
26
int iwl_mld_update_device_power(struct iwl_mld *mld, bool d3)
27
{
28
struct iwl_device_power_cmd cmd = {};
29
bool enable_ps = false;
30
31
if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) {
32
enable_ps = true;
33
34
/* Disable power save if any STA interface has
35
* power save turned off
36
*/
37
ieee80211_iterate_active_interfaces_mtx(mld->hw,
38
IEEE80211_IFACE_ITER_NORMAL,
39
iwl_mld_vif_ps_iterator,
40
&enable_ps);
41
}
42
43
if (enable_ps)
44
cmd.flags |=
45
cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
46
47
if (d3)
48
cmd.flags |=
49
cpu_to_le16(DEVICE_POWER_FLAGS_NO_SLEEP_TILL_D3_MSK);
50
51
IWL_DEBUG_POWER(mld,
52
"Sending device power command with flags = 0x%X\n",
53
cmd.flags);
54
55
return iwl_mld_send_cmd_pdu(mld, POWER_TABLE_CMD, &cmd);
56
}
57
58
int iwl_mld_enable_beacon_filter(struct iwl_mld *mld,
59
const struct ieee80211_bss_conf *link_conf,
60
bool d3)
61
{
62
struct iwl_beacon_filter_cmd cmd = {
63
IWL_BF_CMD_CONFIG_DEFAULTS,
64
.bf_enable_beacon_filter = cpu_to_le32(1),
65
.ba_enable_beacon_abort = cpu_to_le32(1),
66
};
67
68
if (ieee80211_vif_type_p2p(link_conf->vif) != NL80211_IFTYPE_STATION)
69
return 0;
70
71
#ifdef CONFIG_IWLWIFI_DEBUGFS
72
if (iwl_mld_vif_from_mac80211(link_conf->vif)->disable_bf)
73
return 0;
74
#endif
75
76
if (link_conf->cqm_rssi_thold) {
77
cmd.bf_energy_delta =
78
cpu_to_le32(link_conf->cqm_rssi_hyst);
79
/* fw uses an absolute value for this */
80
cmd.bf_roaming_state =
81
cpu_to_le32(-link_conf->cqm_rssi_thold);
82
}
83
84
if (d3)
85
cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);
86
87
return iwl_mld_send_cmd_pdu(mld, REPLY_BEACON_FILTERING_CMD,
88
&cmd);
89
}
90
91
int iwl_mld_disable_beacon_filter(struct iwl_mld *mld,
92
struct ieee80211_vif *vif)
93
{
94
struct iwl_beacon_filter_cmd cmd = {};
95
96
if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)
97
return 0;
98
99
return iwl_mld_send_cmd_pdu(mld, REPLY_BEACON_FILTERING_CMD,
100
&cmd);
101
}
102
103
static bool iwl_mld_power_is_radar(struct iwl_mld *mld,
104
const struct ieee80211_bss_conf *link_conf)
105
{
106
const struct ieee80211_chanctx_conf *chanctx_conf;
107
108
chanctx_conf = wiphy_dereference(mld->wiphy, link_conf->chanctx_conf);
109
110
if (WARN_ON(!chanctx_conf))
111
return false;
112
113
return chanctx_conf->def.chan->flags & IEEE80211_CHAN_RADAR;
114
}
115
116
static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld,
117
struct iwl_mld_link *link,
118
struct iwl_mac_power_cmd *cmd,
119
bool ps_poll)
120
{
121
bool tid_found = false;
122
123
cmd->rx_data_timeout_uapsd =
124
cpu_to_le32(IWL_MLD_UAPSD_RX_DATA_TIMEOUT);
125
cmd->tx_data_timeout_uapsd =
126
cpu_to_le32(IWL_MLD_UAPSD_TX_DATA_TIMEOUT);
127
128
/* set advanced pm flag with no uapsd ACs to enable ps-poll */
129
if (ps_poll) {
130
cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
131
return;
132
}
133
134
for (enum ieee80211_ac_numbers ac = IEEE80211_AC_VO;
135
ac <= IEEE80211_AC_BK;
136
ac++) {
137
if (!link->queue_params[ac].uapsd)
138
continue;
139
140
cmd->flags |=
141
cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK |
142
POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK);
143
144
cmd->uapsd_ac_flags |= BIT(ac);
145
146
/* QNDP TID - the highest TID with no admission control */
147
if (!tid_found && !link->queue_params[ac].acm) {
148
tid_found = true;
149
switch (ac) {
150
case IEEE80211_AC_VO:
151
cmd->qndp_tid = 6;
152
break;
153
case IEEE80211_AC_VI:
154
cmd->qndp_tid = 5;
155
break;
156
case IEEE80211_AC_BE:
157
cmd->qndp_tid = 0;
158
break;
159
case IEEE80211_AC_BK:
160
cmd->qndp_tid = 1;
161
break;
162
}
163
}
164
}
165
166
if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
167
BIT(IEEE80211_AC_VI) |
168
BIT(IEEE80211_AC_BE) |
169
BIT(IEEE80211_AC_BK))) {
170
cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
171
cmd->snooze_interval = cpu_to_le16(IWL_MLD_PS_SNOOZE_INTERVAL);
172
cmd->snooze_window = cpu_to_le16(IWL_MLD_PS_SNOOZE_WINDOW);
173
}
174
175
cmd->uapsd_max_sp = mld->hw->uapsd_max_sp_len;
176
}
177
178
static void
179
iwl_mld_power_config_skip_dtim(struct iwl_mld *mld,
180
const struct ieee80211_bss_conf *link_conf,
181
struct iwl_mac_power_cmd *cmd)
182
{
183
unsigned int dtimper_tu;
184
unsigned int dtimper;
185
unsigned int skip;
186
187
dtimper = link_conf->dtim_period ?: 1;
188
dtimper_tu = dtimper * link_conf->beacon_int;
189
190
if (dtimper >= 10 || iwl_mld_power_is_radar(mld, link_conf))
191
return;
192
193
if (WARN_ON(!dtimper_tu))
194
return;
195
196
/* configure skip over dtim up to 900 TU DTIM interval */
197
skip = max_t(int, 1, 900 / dtimper_tu);
198
199
cmd->skip_dtim_periods = skip;
200
cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
201
}
202
203
#define POWER_KEEP_ALIVE_PERIOD_SEC 25
204
static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
205
struct ieee80211_vif *vif,
206
struct iwl_mac_power_cmd *cmd,
207
bool d3)
208
{
209
int dtimper, bi;
210
int keep_alive;
211
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
212
struct ieee80211_bss_conf *link_conf = &vif->bss_conf;
213
struct iwl_mld_link *link = &mld_vif->deflink;
214
bool ps_poll = false;
215
216
cmd->id_and_color = cpu_to_le32(mld_vif->fw_id);
217
218
if (ieee80211_vif_is_mld(vif)) {
219
int link_id;
220
221
if (WARN_ON(!vif->active_links))
222
return;
223
224
/* The firmware consumes one single configuration for the vif
225
* and can't differentiate between links, just pick the lowest
226
* link_id's configuration and use that.
227
*/
228
link_id = __ffs(vif->active_links);
229
link_conf = link_conf_dereference_check(vif, link_id);
230
link = iwl_mld_link_dereference_check(mld_vif, link_id);
231
232
if (WARN_ON(!link_conf || !link))
233
return;
234
}
235
dtimper = link_conf->dtim_period;
236
bi = link_conf->beacon_int;
237
238
/* Regardless of power management state the driver must set
239
* keep alive period. FW will use it for sending keep alive NDPs
240
* immediately after association. Check that keep alive period
241
* is at least 3 * DTIM
242
*/
243
keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),
244
USEC_PER_SEC);
245
keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);
246
cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
247
248
if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
249
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
250
251
if (!vif->cfg.ps || iwl_mld_tdls_sta_count(mld) > 0)
252
return;
253
254
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
255
256
if (iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) >= 2)
257
cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK);
258
259
/* firmware supports LPRX for beacons at rate 1 Mbps or 6 Mbps only */
260
if (link_conf->beacon_rate &&
261
(link_conf->beacon_rate->bitrate == 10 ||
262
link_conf->beacon_rate->bitrate == 60)) {
263
cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
264
cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
265
}
266
267
if (d3) {
268
iwl_mld_power_config_skip_dtim(mld, link_conf, cmd);
269
cmd->rx_data_timeout =
270
cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT);
271
cmd->tx_data_timeout =
272
cpu_to_le32(IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT);
273
} else if (iwl_mld_vif_low_latency(mld_vif) && vif->p2p) {
274
cmd->tx_data_timeout =
275
cpu_to_le32(IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT);
276
cmd->rx_data_timeout =
277
cpu_to_le32(IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT);
278
} else {
279
cmd->rx_data_timeout =
280
cpu_to_le32(IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT);
281
cmd->tx_data_timeout =
282
cpu_to_le32(IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT);
283
}
284
285
/* uAPSD is only enabled for specific certifications. For those cases,
286
* mac80211 will allow uAPSD. Always call iwl_mld_power_configure_uapsd
287
* which will look at what mac80211 is saying.
288
*/
289
#ifdef CONFIG_IWLWIFI_DEBUGFS
290
ps_poll = mld_vif->use_ps_poll;
291
#endif
292
iwl_mld_power_configure_uapsd(mld, link, cmd, ps_poll);
293
}
294
295
int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif,
296
bool d3)
297
{
298
struct iwl_mac_power_cmd cmd = {};
299
300
iwl_mld_power_build_cmd(mld, vif, &cmd, d3);
301
302
return iwl_mld_send_cmd_pdu(mld, MAC_PM_POWER_TABLE, &cmd);
303
}
304
305
static void
306
iwl_mld_tpe_sta_cmd_data(struct iwl_txpower_constraints_cmd *cmd,
307
const struct ieee80211_bss_conf *link)
308
{
309
u8 i;
310
311
/* NOTE: the 0 here is IEEE80211_TPE_CAT_6GHZ_DEFAULT,
312
* we fully ignore IEEE80211_TPE_CAT_6GHZ_SUBORDINATE
313
*/
314
315
BUILD_BUG_ON(ARRAY_SIZE(cmd->psd_pwr) !=
316
ARRAY_SIZE(link->tpe.psd_local[0].power));
317
318
/* if not valid, mac80211 puts default (max value) */
319
for (i = 0; i < ARRAY_SIZE(cmd->psd_pwr); i++)
320
cmd->psd_pwr[i] = min(link->tpe.psd_local[0].power[i],
321
link->tpe.psd_reg_client[0].power[i]);
322
323
BUILD_BUG_ON(ARRAY_SIZE(cmd->eirp_pwr) !=
324
ARRAY_SIZE(link->tpe.max_local[0].power));
325
326
for (i = 0; i < ARRAY_SIZE(cmd->eirp_pwr); i++)
327
cmd->eirp_pwr[i] = min(link->tpe.max_local[0].power[i],
328
link->tpe.max_reg_client[0].power[i]);
329
}
330
331
void
332
iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld,
333
struct ieee80211_vif *vif,
334
struct ieee80211_bss_conf *link)
335
{
336
struct iwl_txpower_constraints_cmd cmd = {};
337
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
338
int ret;
339
340
lockdep_assert_wiphy(mld->wiphy);
341
342
if (!mld_link->active)
343
return;
344
345
if (link->chanreq.oper.chan->band != NL80211_BAND_6GHZ)
346
return;
347
348
cmd.link_id = cpu_to_le16(mld_link->fw_id);
349
memset(cmd.psd_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.psd_pwr));
350
memset(cmd.eirp_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.eirp_pwr));
351
352
if (vif->type == NL80211_IFTYPE_AP) {
353
cmd.ap_type = cpu_to_le16(IWL_6GHZ_AP_TYPE_VLP);
354
} else if (link->power_type == IEEE80211_REG_UNSET_AP) {
355
return;
356
} else {
357
cmd.ap_type = cpu_to_le16(link->power_type - 1);
358
iwl_mld_tpe_sta_cmd_data(&cmd, link);
359
}
360
361
ret = iwl_mld_send_cmd_pdu(mld,
362
WIDE_ID(PHY_OPS_GROUP,
363
AP_TX_POWER_CONSTRAINTS_CMD),
364
&cmd);
365
if (ret)
366
IWL_ERR(mld,
367
"failed to send AP_TX_POWER_CONSTRAINTS_CMD (%d)\n",
368
ret);
369
}
370
371
int iwl_mld_set_tx_power(struct iwl_mld *mld,
372
struct ieee80211_bss_conf *link_conf,
373
s16 tx_power)
374
{
375
u32 cmd_id = REDUCE_TX_POWER_CMD;
376
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
377
u16 u_tx_power = tx_power == IWL_DEFAULT_MAX_TX_POWER ?
378
IWL_DEV_MAX_TX_POWER : 8 * tx_power;
379
struct iwl_dev_tx_power_cmd cmd = {
380
.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK),
381
.common.pwr_restriction = cpu_to_le16(u_tx_power),
382
};
383
int len = sizeof(cmd.common) + sizeof(cmd.v10);
384
385
if (WARN_ON(!mld_link))
386
return -ENODEV;
387
388
cmd.common.link_id = cpu_to_le32(mld_link->fw_id);
389
390
return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len);
391
}
392
393