]>
Commit | Line | Data |
---|---|---|
8ca151b5 JB |
1 | /****************************************************************************** |
2 | * | |
3 | * This file is provided under a dual BSD/GPLv2 license. When using or | |
4 | * redistributing this file, you may do so under either license. | |
5 | * | |
6 | * GPL LICENSE SUMMARY | |
7 | * | |
51368bf7 | 8 | * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. |
8b4139dc | 9 | * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH |
702e975d | 10 | * Copyright(c) 2015 - 2017 Intel Deutschland GmbH |
48e775e6 | 11 | * Copyright (C) 2018 - 2019 Intel Corporation |
8ca151b5 JB |
12 | * |
13 | * This program is free software; you can redistribute it and/or modify | |
14 | * it under the terms of version 2 of the GNU General Public License as | |
15 | * published by the Free Software Foundation. | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, but | |
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
20 | * General Public License for more details. | |
21 | * | |
8ca151b5 | 22 | * The full GNU General Public License is included in this distribution |
410dc5aa | 23 | * in the file called COPYING. |
8ca151b5 JB |
24 | * |
25 | * Contact Information: | |
cb2f8277 | 26 | * Intel Linux Wireless <linuxwifi@intel.com> |
8ca151b5 JB |
27 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 |
28 | * | |
29 | * BSD LICENSE | |
30 | * | |
51368bf7 | 31 | * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. |
8b4139dc | 32 | * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH |
702e975d | 33 | * Copyright(c) 2015 - 2017 Intel Deutschland GmbH |
48e775e6 | 34 | * Copyright (C) 2018 - 2019 Intel Corporation |
8ca151b5 JB |
35 | * All rights reserved. |
36 | * | |
37 | * Redistribution and use in source and binary forms, with or without | |
38 | * modification, are permitted provided that the following conditions | |
39 | * are met: | |
40 | * | |
41 | * * Redistributions of source code must retain the above copyright | |
42 | * notice, this list of conditions and the following disclaimer. | |
43 | * * Redistributions in binary form must reproduce the above copyright | |
44 | * notice, this list of conditions and the following disclaimer in | |
45 | * the documentation and/or other materials provided with the | |
46 | * distribution. | |
47 | * * Neither the name Intel Corporation nor the names of its | |
48 | * contributors may be used to endorse or promote products derived | |
49 | * from this software without specific prior written permission. | |
50 | * | |
51 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
52 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
53 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
54 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
55 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
56 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
57 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
58 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
59 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
60 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
61 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
62 | * | |
63 | *****************************************************************************/ | |
64 | ||
65 | #include <linux/kernel.h> | |
66 | #include <linux/module.h> | |
67 | #include <linux/slab.h> | |
93803b33 | 68 | #include <linux/etherdevice.h> |
8ca151b5 JB |
69 | |
70 | #include <net/mac80211.h> | |
71 | ||
72 | #include "iwl-debug.h" | |
73 | #include "mvm.h" | |
74 | #include "iwl-modparams.h" | |
d172a5ef | 75 | #include "fw/api/power.h" |
8ca151b5 JB |
76 | |
77 | #define POWER_KEEP_ALIVE_PERIOD_SEC 25 | |
78 | ||
d623d24a | 79 | static |
e811ada7 | 80 | int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, |
3dd37d05 EP |
81 | struct iwl_beacon_filter_cmd *cmd, |
82 | u32 flags) | |
071d4990 | 83 | { |
537ea3bb AG |
84 | u16 len; |
85 | ||
d623d24a EG |
86 | IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", |
87 | le32_to_cpu(cmd->ba_enable_beacon_abort)); | |
88 | IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", | |
89 | le32_to_cpu(cmd->ba_escape_timer)); | |
90 | IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n", | |
91 | le32_to_cpu(cmd->bf_debug_flag)); | |
92 | IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n", | |
93 | le32_to_cpu(cmd->bf_enable_beacon_filter)); | |
94 | IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n", | |
95 | le32_to_cpu(cmd->bf_energy_delta)); | |
96 | IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n", | |
97 | le32_to_cpu(cmd->bf_escape_timer)); | |
98 | IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n", | |
99 | le32_to_cpu(cmd->bf_roaming_energy_delta)); | |
100 | IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n", | |
101 | le32_to_cpu(cmd->bf_roaming_state)); | |
102 | IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n", | |
103 | le32_to_cpu(cmd->bf_temp_threshold)); | |
104 | IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n", | |
105 | le32_to_cpu(cmd->bf_temp_fast_filter)); | |
106 | IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n", | |
107 | le32_to_cpu(cmd->bf_temp_slow_filter)); | |
537ea3bb AG |
108 | IWL_DEBUG_POWER(mvm, "bf_threshold_absolute_low is: %d, %d\n", |
109 | le32_to_cpu(cmd->bf_threshold_absolute_low[0]), | |
110 | le32_to_cpu(cmd->bf_threshold_absolute_low[1])); | |
111 | ||
112 | IWL_DEBUG_POWER(mvm, "bf_threshold_absolute_high is: %d, %d\n", | |
113 | le32_to_cpu(cmd->bf_threshold_absolute_high[0]), | |
114 | le32_to_cpu(cmd->bf_threshold_absolute_high[1])); | |
115 | ||
116 | if (fw_has_api(&mvm->fw->ucode_capa, | |
117 | IWL_UCODE_TLV_API_BEACON_FILTER_V4)) | |
118 | len = sizeof(struct iwl_beacon_filter_cmd); | |
119 | else | |
120 | len = offsetof(struct iwl_beacon_filter_cmd, | |
121 | bf_threshold_absolute_low); | |
d623d24a EG |
122 | |
123 | return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags, | |
537ea3bb | 124 | len, cmd); |
071d4990 AB |
125 | } |
126 | ||
a20fd398 AO |
127 | static |
128 | void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm, | |
129 | struct ieee80211_vif *vif, | |
632fa0ea | 130 | struct iwl_beacon_filter_cmd *cmd) |
a20fd398 AO |
131 | { |
132 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
133 | ||
632fa0ea | 134 | if (vif->bss_conf.cqm_rssi_thold) { |
a20fd398 AO |
135 | cmd->bf_energy_delta = |
136 | cpu_to_le32(vif->bss_conf.cqm_rssi_hyst); | |
137 | /* fw uses an absolute value for this */ | |
138 | cmd->bf_roaming_state = | |
139 | cpu_to_le32(-vif->bss_conf.cqm_rssi_thold); | |
140 | } | |
141 | cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled); | |
142 | } | |
143 | ||
3f0b2b3e | 144 | static void iwl_mvm_power_log(struct iwl_mvm *mvm, |
e811ada7 | 145 | struct iwl_mac_power_cmd *cmd) |
3f0b2b3e AB |
146 | { |
147 | IWL_DEBUG_POWER(mvm, | |
e811ada7 AB |
148 | "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n", |
149 | cmd->id_and_color, iwlmvm_mod_params.power_scheme, | |
3f0b2b3e | 150 | le16_to_cpu(cmd->flags)); |
e811ada7 AB |
151 | IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", |
152 | le16_to_cpu(cmd->keep_alive_seconds)); | |
3f0b2b3e | 153 | |
e3c588ec AB |
154 | if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) { |
155 | IWL_DEBUG_POWER(mvm, "Disable power management\n"); | |
156 | return; | |
157 | } | |
158 | ||
159 | IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n", | |
160 | le32_to_cpu(cmd->rx_data_timeout)); | |
161 | IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n", | |
162 | le32_to_cpu(cmd->tx_data_timeout)); | |
163 | if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) | |
164 | IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n", | |
165 | cmd->skip_dtim_periods); | |
166 | if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) | |
167 | IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", | |
168 | cmd->lprx_rssi_threshold); | |
169 | if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { | |
170 | IWL_DEBUG_POWER(mvm, "uAPSD enabled\n"); | |
171 | IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n", | |
172 | le32_to_cpu(cmd->rx_data_timeout_uapsd)); | |
173 | IWL_DEBUG_POWER(mvm, "Tx timeout (uAPSD) = %u usec\n", | |
174 | le32_to_cpu(cmd->tx_data_timeout_uapsd)); | |
175 | IWL_DEBUG_POWER(mvm, "QNDP TID = %d\n", cmd->qndp_tid); | |
176 | IWL_DEBUG_POWER(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags); | |
177 | IWL_DEBUG_POWER(mvm, "Max SP = %d\n", cmd->uapsd_max_sp); | |
3f0b2b3e AB |
178 | } |
179 | } | |
180 | ||
175a70b7 AB |
181 | static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, |
182 | struct ieee80211_vif *vif, | |
183 | struct iwl_mac_power_cmd *cmd) | |
184 | { | |
185 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
186 | enum ieee80211_ac_numbers ac; | |
187 | bool tid_found = false; | |
188 | ||
143b0b2a EG |
189 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
190 | /* set advanced pm flag with no uapsd ACs to enable ps-poll */ | |
191 | if (mvmvif->dbgfs_pm.use_ps_poll) { | |
192 | cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); | |
193 | return; | |
194 | } | |
195 | #endif | |
196 | ||
175a70b7 AB |
197 | for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) { |
198 | if (!mvmvif->queue_params[ac].uapsd) | |
199 | continue; | |
200 | ||
702e975d | 201 | if (mvm->fwrt.cur_fw_img != IWL_UCODE_WOWLAN) |
175a70b7 AB |
202 | cmd->flags |= |
203 | cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); | |
204 | ||
205 | cmd->uapsd_ac_flags |= BIT(ac); | |
206 | ||
207 | /* QNDP TID - the highest TID with no admission control */ | |
208 | if (!tid_found && !mvmvif->queue_params[ac].acm) { | |
209 | tid_found = true; | |
210 | switch (ac) { | |
211 | case IEEE80211_AC_VO: | |
212 | cmd->qndp_tid = 6; | |
213 | break; | |
214 | case IEEE80211_AC_VI: | |
215 | cmd->qndp_tid = 5; | |
216 | break; | |
217 | case IEEE80211_AC_BE: | |
218 | cmd->qndp_tid = 0; | |
219 | break; | |
220 | case IEEE80211_AC_BK: | |
221 | cmd->qndp_tid = 1; | |
222 | break; | |
223 | } | |
224 | } | |
225 | } | |
226 | ||
175a70b7 AB |
227 | cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK); |
228 | ||
229 | if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) | | |
230 | BIT(IEEE80211_AC_VI) | | |
231 | BIT(IEEE80211_AC_BE) | | |
232 | BIT(IEEE80211_AC_BK))) { | |
233 | cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); | |
234 | cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL); | |
702e975d JB |
235 | cmd->snooze_window = |
236 | (mvm->fwrt.cur_fw_img == IWL_UCODE_WOWLAN) ? | |
237 | cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) : | |
238 | cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW); | |
175a70b7 AB |
239 | } |
240 | ||
6e2611f3 | 241 | cmd->uapsd_max_sp = mvm->hw->uapsd_max_sp_len; |
175a70b7 | 242 | |
702e975d | 243 | if (mvm->fwrt.cur_fw_img == IWL_UCODE_WOWLAN || cmd->flags & |
175a70b7 AB |
244 | cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { |
245 | cmd->rx_data_timeout_uapsd = | |
246 | cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); | |
247 | cmd->tx_data_timeout_uapsd = | |
248 | cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); | |
249 | } else { | |
250 | cmd->rx_data_timeout_uapsd = | |
251 | cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT); | |
252 | cmd->tx_data_timeout_uapsd = | |
253 | cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT); | |
254 | } | |
255 | ||
256 | if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { | |
257 | cmd->heavy_tx_thld_packets = | |
258 | IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS; | |
259 | cmd->heavy_rx_thld_packets = | |
260 | IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS; | |
261 | } else { | |
262 | cmd->heavy_tx_thld_packets = | |
263 | IWL_MVM_PS_HEAVY_TX_THLD_PACKETS; | |
264 | cmd->heavy_rx_thld_packets = | |
265 | IWL_MVM_PS_HEAVY_RX_THLD_PACKETS; | |
266 | } | |
267 | cmd->heavy_tx_thld_percentage = | |
268 | IWL_MVM_PS_HEAVY_TX_THLD_PERCENT; | |
269 | cmd->heavy_rx_thld_percentage = | |
270 | IWL_MVM_PS_HEAVY_RX_THLD_PERCENT; | |
271 | } | |
272 | ||
ee95ed37 AA |
273 | static void iwl_mvm_p2p_standalone_iterator(void *_data, u8 *mac, |
274 | struct ieee80211_vif *vif) | |
275 | { | |
276 | bool *is_p2p_standalone = _data; | |
277 | ||
278 | switch (ieee80211_vif_type_p2p(vif)) { | |
279 | case NL80211_IFTYPE_P2P_GO: | |
280 | case NL80211_IFTYPE_AP: | |
281 | *is_p2p_standalone = false; | |
282 | break; | |
283 | case NL80211_IFTYPE_STATION: | |
284 | if (vif->bss_conf.assoc) | |
285 | *is_p2p_standalone = false; | |
286 | break; | |
287 | ||
288 | default: | |
289 | break; | |
290 | } | |
291 | } | |
292 | ||
c6e37a68 AA |
293 | static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, |
294 | struct ieee80211_vif *vif) | |
295 | { | |
296 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
297 | ||
298 | if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, | |
299 | ETH_ALEN)) | |
300 | return false; | |
301 | ||
c6e37a68 AA |
302 | /* |
303 | * Avoid using uAPSD if P2P client is associated to GO that uses | |
304 | * opportunistic power save. This is due to current FW limitation. | |
305 | */ | |
306 | if (vif->p2p && | |
307 | (vif->bss_conf.p2p_noa_attr.oppps_ctwindow & | |
308 | IEEE80211_P2P_OPPPS_ENABLE_BIT)) | |
309 | return false; | |
310 | ||
0534528e AA |
311 | /* |
312 | * Avoid using uAPSD if client is in DCM - | |
313 | * low latency issue in Miracast | |
314 | */ | |
cf7b491d | 315 | if (iwl_mvm_phy_ctx_count(mvm) >= 2) |
0534528e AA |
316 | return false; |
317 | ||
ee95ed37 AA |
318 | if (vif->p2p) { |
319 | /* Allow U-APSD only if p2p is stand alone */ | |
320 | bool is_p2p_standalone = true; | |
321 | ||
c5241b0c | 322 | if (!iwl_mvm_is_p2p_scm_uapsd_supported(mvm)) |
ee95ed37 AA |
323 | return false; |
324 | ||
325 | ieee80211_iterate_active_interfaces_atomic(mvm->hw, | |
326 | IEEE80211_IFACE_ITER_NORMAL, | |
327 | iwl_mvm_p2p_standalone_iterator, | |
328 | &is_p2p_standalone); | |
329 | ||
330 | if (!is_p2p_standalone) | |
331 | return false; | |
332 | } | |
333 | ||
c6e37a68 AA |
334 | return true; |
335 | } | |
336 | ||
49c6d802 AA |
337 | static bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif) |
338 | { | |
339 | struct ieee80211_chanctx_conf *chanctx_conf; | |
340 | struct ieee80211_channel *chan; | |
341 | bool radar_detect = false; | |
342 | ||
343 | rcu_read_lock(); | |
344 | chanctx_conf = rcu_dereference(vif->chanctx_conf); | |
345 | WARN_ON(!chanctx_conf); | |
346 | if (chanctx_conf) { | |
347 | chan = chanctx_conf->def.chan; | |
348 | radar_detect = chan->flags & IEEE80211_CHAN_RADAR; | |
349 | } | |
350 | rcu_read_unlock(); | |
351 | ||
352 | return radar_detect; | |
353 | } | |
354 | ||
a6449126 JB |
355 | static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm, |
356 | struct ieee80211_vif *vif, | |
357 | struct iwl_mac_power_cmd *cmd, | |
358 | bool host_awake) | |
359 | { | |
360 | int dtimper = vif->bss_conf.dtim_period ?: 1; | |
361 | int skip; | |
362 | ||
363 | /* disable, in case we're supposed to override */ | |
364 | cmd->skip_dtim_periods = 0; | |
365 | cmd->flags &= ~cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); | |
366 | ||
367 | if (iwl_mvm_power_is_radar(vif)) | |
368 | return; | |
369 | ||
370 | if (dtimper >= 10) | |
371 | return; | |
372 | ||
a6449126 JB |
373 | if (host_awake) { |
374 | if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_LP) | |
375 | return; | |
376 | skip = 2; | |
377 | } else { | |
378 | int dtimper_tu = dtimper * vif->bss_conf.beacon_int; | |
379 | ||
380 | if (WARN_ON(!dtimper_tu)) | |
381 | return; | |
382 | /* configure skip over dtim up to 306TU - 314 msec */ | |
383 | skip = max_t(u8, 1, 306 / dtimper_tu); | |
384 | } | |
385 | ||
386 | /* the firmware really expects "look at every X DTIMs", so add 1 */ | |
387 | cmd->skip_dtim_periods = 1 + skip; | |
388 | cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); | |
389 | } | |
390 | ||
e811ada7 AB |
391 | static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, |
392 | struct ieee80211_vif *vif, | |
9645edb6 JB |
393 | struct iwl_mac_power_cmd *cmd, |
394 | bool host_awake) | |
8ca151b5 | 395 | { |
5387b348 | 396 | int dtimper, bi; |
8ca151b5 | 397 | int keep_alive; |
b571a697 AB |
398 | struct iwl_mvm_vif *mvmvif __maybe_unused = |
399 | iwl_mvm_vif_from_mac80211(vif); | |
8ca151b5 | 400 | |
e811ada7 AB |
401 | cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, |
402 | mvmvif->color)); | |
717e2390 | 403 | dtimper = vif->bss_conf.dtim_period; |
886ca9f5 | 404 | bi = vif->bss_conf.beacon_int; |
e811ada7 | 405 | |
e16cf7ec AB |
406 | /* |
407 | * Regardless of power management state the driver must set | |
408 | * keep alive period. FW will use it for sending keep alive NDPs | |
e811ada7 AB |
409 | * immediately after association. Check that keep alive period |
410 | * is at least 3 * DTIM | |
e16cf7ec | 411 | */ |
5387b348 AA |
412 | keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi), |
413 | USEC_PER_SEC); | |
414 | keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC); | |
e811ada7 | 415 | cmd->keep_alive_seconds = cpu_to_le16(keep_alive); |
e16cf7ec | 416 | |
e5e7aa8e | 417 | if (mvm->ps_disabled) |
8ca151b5 JB |
418 | return; |
419 | ||
9a613048 AB |
420 | cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); |
421 | ||
b08dbed7 JB |
422 | if (!vif->bss_conf.ps || !mvmvif->pm_enabled) |
423 | return; | |
424 | ||
425 | if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p && | |
426 | (!fw_has_capa(&mvm->fw->ucode_capa, | |
427 | IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS) || | |
428 | !IWL_MVM_P2P_LOWLATENCY_PS_ENABLE)) | |
9a613048 AB |
429 | return; |
430 | ||
431 | cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); | |
8ca151b5 | 432 | |
bd4ace2a AB |
433 | if (vif->bss_conf.beacon_rate && |
434 | (vif->bss_conf.beacon_rate->bitrate == 10 || | |
435 | vif->bss_conf.beacon_rate->bitrate == 60)) { | |
436 | cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); | |
e811ada7 | 437 | cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD; |
bd4ace2a AB |
438 | } |
439 | ||
9645edb6 | 440 | iwl_mvm_power_config_skip_dtim(mvm, vif, cmd, host_awake); |
8ca151b5 | 441 | |
b08dbed7 | 442 | if (!host_awake) { |
99545924 | 443 | cmd->rx_data_timeout = |
b08dbed7 | 444 | cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); |
99545924 | 445 | cmd->tx_data_timeout = |
b08dbed7 JB |
446 | cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); |
447 | } else if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p && | |
448 | fw_has_capa(&mvm->fw->ucode_capa, | |
449 | IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS)) { | |
450 | cmd->tx_data_timeout = | |
451 | cpu_to_le32(IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT); | |
452 | cmd->rx_data_timeout = | |
453 | cpu_to_le32(IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT); | |
ee1e8422 | 454 | } else { |
99545924 | 455 | cmd->rx_data_timeout = |
b08dbed7 | 456 | cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT); |
99545924 | 457 | cmd->tx_data_timeout = |
b08dbed7 | 458 | cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT); |
ee1e8422 | 459 | } |
b571a697 | 460 | |
c6e37a68 | 461 | if (iwl_mvm_power_allow_uapsd(mvm, vif)) |
175a70b7 | 462 | iwl_mvm_power_configure_uapsd(mvm, vif, cmd); |
e3c588ec | 463 | |
b571a697 AB |
464 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
465 | if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) | |
e811ada7 AB |
466 | cmd->keep_alive_seconds = |
467 | cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds); | |
b571a697 AB |
468 | if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) { |
469 | if (mvmvif->dbgfs_pm.skip_over_dtim) | |
470 | cmd->flags |= | |
471 | cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); | |
472 | else | |
473 | cmd->flags &= | |
474 | cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK); | |
475 | } | |
476 | if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT) | |
477 | cmd->rx_data_timeout = | |
478 | cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout); | |
479 | if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT) | |
480 | cmd->tx_data_timeout = | |
481 | cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout); | |
482 | if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS) | |
e811ada7 | 483 | cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods; |
bd4ace2a AB |
484 | if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) { |
485 | if (mvmvif->dbgfs_pm.lprx_ena) | |
486 | cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); | |
487 | else | |
488 | cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK); | |
489 | } | |
490 | if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD) | |
e811ada7 | 491 | cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold; |
89716344 AB |
492 | if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) { |
493 | if (mvmvif->dbgfs_pm.snooze_ena) | |
494 | cmd->flags |= | |
495 | cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); | |
496 | else | |
497 | cmd->flags &= | |
498 | cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK); | |
499 | } | |
e45a941d AB |
500 | if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) { |
501 | u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK; | |
502 | if (mvmvif->dbgfs_pm.uapsd_misbehaving) | |
503 | cmd->flags |= cpu_to_le16(flag); | |
504 | else | |
505 | cmd->flags &= cpu_to_le16(flag); | |
506 | } | |
b571a697 | 507 | #endif /* CONFIG_IWLWIFI_DEBUGFS */ |
8ca151b5 JB |
508 | } |
509 | ||
e5e7aa8e | 510 | static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm, |
e811ada7 | 511 | struct ieee80211_vif *vif) |
8ca151b5 | 512 | { |
e811ada7 | 513 | struct iwl_mac_power_cmd cmd = {}; |
8ca151b5 | 514 | |
9645edb6 | 515 | iwl_mvm_power_build_cmd(mvm, vif, &cmd, |
702e975d | 516 | mvm->fwrt.cur_fw_img != IWL_UCODE_WOWLAN); |
3f0b2b3e | 517 | iwl_mvm_power_log(mvm, &cmd); |
474b50c3 EG |
518 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
519 | memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd)); | |
520 | #endif | |
8ca151b5 | 521 | |
a1022927 | 522 | return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, 0, |
06280a2b EG |
523 | sizeof(cmd), &cmd); |
524 | } | |
525 | ||
e5e7aa8e | 526 | int iwl_mvm_power_update_device(struct iwl_mvm *mvm) |
64b928c4 AB |
527 | { |
528 | struct iwl_device_power_cmd cmd = { | |
ceef91c8 | 529 | .flags = 0, |
64b928c4 AB |
530 | }; |
531 | ||
e5e7aa8e EG |
532 | if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) |
533 | mvm->ps_disabled = true; | |
534 | ||
ceef91c8 JB |
535 | if (!mvm->ps_disabled) |
536 | cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); | |
64b928c4 AB |
537 | |
538 | #ifdef CONFIG_IWLWIFI_DEBUGFS | |
702e975d JB |
539 | if ((mvm->fwrt.cur_fw_img == IWL_UCODE_WOWLAN) ? |
540 | mvm->disable_power_off_d3 : mvm->disable_power_off) | |
64b928c4 AB |
541 | cmd.flags &= |
542 | cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); | |
543 | #endif | |
48e775e6 HD |
544 | if (mvm->ext_clock_valid) |
545 | cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_32K_CLK_VALID_MSK); | |
546 | ||
64b928c4 AB |
547 | IWL_DEBUG_POWER(mvm, |
548 | "Sending device power command with flags = 0x%X\n", | |
549 | cmd.flags); | |
550 | ||
a1022927 | 551 | return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, 0, sizeof(cmd), |
64b928c4 AB |
552 | &cmd); |
553 | } | |
554 | ||
175a70b7 AB |
555 | void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) |
556 | { | |
557 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
558 | ||
559 | if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid, | |
560 | ETH_ALEN)) | |
93803b33 | 561 | eth_zero_addr(mvmvif->uapsd_misbehaving_bssid); |
175a70b7 AB |
562 | } |
563 | ||
564 | static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac, | |
565 | struct ieee80211_vif *vif) | |
566 | { | |
567 | u8 *ap_sta_id = _data; | |
568 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
569 | ||
570 | /* The ap_sta_id is not expected to change during current association | |
571 | * so no explicit protection is needed | |
572 | */ | |
573 | if (mvmvif->ap_sta_id == *ap_sta_id) | |
574 | memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, | |
575 | ETH_ALEN); | |
576 | } | |
577 | ||
0416841d JB |
578 | void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, |
579 | struct iwl_rx_cmd_buffer *rxb) | |
175a70b7 AB |
580 | { |
581 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | |
582 | struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data; | |
583 | u8 ap_sta_id = le32_to_cpu(notif->sta_id); | |
584 | ||
585 | ieee80211_iterate_active_interfaces_atomic( | |
586 | mvm->hw, IEEE80211_IFACE_ITER_NORMAL, | |
587 | iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id); | |
175a70b7 AB |
588 | } |
589 | ||
19889025 | 590 | struct iwl_power_vifs { |
fa3d07e4 | 591 | struct iwl_mvm *mvm; |
e5e7aa8e | 592 | struct ieee80211_vif *bss_vif; |
8ea0c68f | 593 | struct ieee80211_vif *p2p_vif; |
19889025 AA |
594 | struct ieee80211_vif *ap_vif; |
595 | struct ieee80211_vif *monitor_vif; | |
596 | bool p2p_active; | |
597 | bool bss_active; | |
598 | bool ap_active; | |
599 | bool monitor_active; | |
06280a2b EG |
600 | }; |
601 | ||
128aa948 LC |
602 | static void iwl_mvm_power_disable_pm_iterator(void *_data, u8* mac, |
603 | struct ieee80211_vif *vif) | |
1c2abf72 | 604 | { |
06280a2b | 605 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
e5e7aa8e | 606 | |
19889025 | 607 | mvmvif->pm_enabled = false; |
128aa948 LC |
608 | } |
609 | ||
2533edce LC |
610 | static void iwl_mvm_power_ps_disabled_iterator(void *_data, u8* mac, |
611 | struct ieee80211_vif *vif) | |
612 | { | |
613 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
614 | bool *disable_ps = _data; | |
615 | ||
4f23f206 SS |
616 | if (mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < NUM_PHY_CTX) |
617 | *disable_ps |= mvmvif->ps_disabled; | |
2533edce LC |
618 | } |
619 | ||
128aa948 LC |
620 | static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac, |
621 | struct ieee80211_vif *vif) | |
622 | { | |
623 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
624 | struct iwl_power_vifs *power_iterator = _data; | |
4f23f206 | 625 | bool active = mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < NUM_PHY_CTX; |
128aa948 | 626 | |
e5e7aa8e EG |
627 | switch (ieee80211_vif_type_p2p(vif)) { |
628 | case NL80211_IFTYPE_P2P_DEVICE: | |
629 | break; | |
630 | ||
631 | case NL80211_IFTYPE_P2P_GO: | |
632 | case NL80211_IFTYPE_AP: | |
19889025 AA |
633 | /* only a single MAC of the same type */ |
634 | WARN_ON(power_iterator->ap_vif); | |
635 | power_iterator->ap_vif = vif; | |
4f23f206 SS |
636 | if (active) |
637 | power_iterator->ap_active = true; | |
e5e7aa8e EG |
638 | break; |
639 | ||
640 | case NL80211_IFTYPE_MONITOR: | |
19889025 AA |
641 | /* only a single MAC of the same type */ |
642 | WARN_ON(power_iterator->monitor_vif); | |
643 | power_iterator->monitor_vif = vif; | |
4f23f206 SS |
644 | if (active) |
645 | power_iterator->monitor_active = true; | |
e5e7aa8e EG |
646 | break; |
647 | ||
648 | case NL80211_IFTYPE_P2P_CLIENT: | |
19889025 | 649 | /* only a single MAC of the same type */ |
8ea0c68f AA |
650 | WARN_ON(power_iterator->p2p_vif); |
651 | power_iterator->p2p_vif = vif; | |
4f23f206 SS |
652 | if (active) |
653 | power_iterator->p2p_active = true; | |
e5e7aa8e EG |
654 | break; |
655 | ||
656 | case NL80211_IFTYPE_STATION: | |
e5e7aa8e | 657 | power_iterator->bss_vif = vif; |
4f23f206 SS |
658 | if (active) |
659 | power_iterator->bss_active = true; | |
e5e7aa8e EG |
660 | break; |
661 | ||
662 | default: | |
663 | break; | |
664 | } | |
665 | } | |
1c2abf72 | 666 | |
b1873300 LC |
667 | static void iwl_mvm_power_set_pm(struct iwl_mvm *mvm, |
668 | struct iwl_power_vifs *vifs) | |
e5e7aa8e | 669 | { |
19889025 AA |
670 | struct iwl_mvm_vif *bss_mvmvif = NULL; |
671 | struct iwl_mvm_vif *p2p_mvmvif = NULL; | |
672 | struct iwl_mvm_vif *ap_mvmvif = NULL; | |
673 | bool client_same_channel = false; | |
674 | bool ap_same_channel = false; | |
e5e7aa8e | 675 | |
19889025 | 676 | lockdep_assert_held(&mvm->mutex); |
06280a2b | 677 | |
128aa948 | 678 | /* set pm_enable to false */ |
e5e7aa8e | 679 | ieee80211_iterate_active_interfaces_atomic(mvm->hw, |
128aa948 LC |
680 | IEEE80211_IFACE_ITER_NORMAL, |
681 | iwl_mvm_power_disable_pm_iterator, | |
682 | NULL); | |
19889025 AA |
683 | |
684 | if (vifs->bss_vif) | |
685 | bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif); | |
686 | ||
687 | if (vifs->p2p_vif) | |
688 | p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif); | |
689 | ||
690 | if (vifs->ap_vif) | |
691 | ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif); | |
692 | ||
fcf23352 AN |
693 | /* don't allow PM if any TDLS stations exist */ |
694 | if (iwl_mvm_tdls_sta_count(mvm, NULL)) | |
695 | return; | |
696 | ||
19889025 | 697 | /* enable PM on bss if bss stand alone */ |
9474d444 | 698 | if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) { |
19889025 AA |
699 | bss_mvmvif->pm_enabled = true; |
700 | return; | |
701 | } | |
702 | ||
703 | /* enable PM on p2p if p2p stand alone */ | |
9474d444 | 704 | if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) { |
4b87e5af | 705 | p2p_mvmvif->pm_enabled = true; |
19889025 AA |
706 | return; |
707 | } | |
708 | ||
709 | if (vifs->bss_active && vifs->p2p_active) | |
710 | client_same_channel = (bss_mvmvif->phy_ctxt->id == | |
711 | p2p_mvmvif->phy_ctxt->id); | |
712 | if (vifs->bss_active && vifs->ap_active) | |
713 | ap_same_channel = (bss_mvmvif->phy_ctxt->id == | |
714 | ap_mvmvif->phy_ctxt->id); | |
715 | ||
63ef81cf | 716 | /* clients are not stand alone: enable PM if DCM */ |
4b87e5af | 717 | if (!(client_same_channel || ap_same_channel)) { |
63ef81cf | 718 | if (vifs->bss_active) |
19889025 | 719 | bss_mvmvif->pm_enabled = true; |
4b87e5af | 720 | if (vifs->p2p_active) |
63ef81cf AA |
721 | p2p_mvmvif->pm_enabled = true; |
722 | return; | |
19889025 AA |
723 | } |
724 | ||
725 | /* | |
726 | * There is only one channel in the system and there are only | |
727 | * bss and p2p clients that share it | |
728 | */ | |
4b87e5af | 729 | if (client_same_channel && !vifs->ap_active) { |
19889025 AA |
730 | /* share same channel*/ |
731 | bss_mvmvif->pm_enabled = true; | |
4b87e5af | 732 | p2p_mvmvif->pm_enabled = true; |
19889025 | 733 | } |
1c2abf72 AB |
734 | } |
735 | ||
b571a697 | 736 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
c1cb92fc EG |
737 | int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, |
738 | struct ieee80211_vif *vif, char *buf, | |
739 | int bufsz) | |
e811ada7 | 740 | { |
474b50c3 | 741 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
e811ada7 AB |
742 | struct iwl_mac_power_cmd cmd = {}; |
743 | int pos = 0; | |
744 | ||
474b50c3 EG |
745 | mutex_lock(&mvm->mutex); |
746 | memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd)); | |
747 | mutex_unlock(&mvm->mutex); | |
e811ada7 | 748 | |
e811ada7 AB |
749 | pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", |
750 | iwlmvm_mod_params.power_scheme); | |
751 | pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n", | |
752 | le16_to_cpu(cmd.flags)); | |
753 | pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", | |
754 | le16_to_cpu(cmd.keep_alive_seconds)); | |
755 | ||
175a70b7 AB |
756 | if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) |
757 | return pos; | |
758 | ||
759 | pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", | |
760 | (cmd.flags & | |
761 | cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0); | |
762 | pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", | |
763 | cmd.skip_dtim_periods); | |
764 | if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { | |
765 | pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n", | |
766 | le32_to_cpu(cmd.rx_data_timeout)); | |
767 | pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n", | |
768 | le32_to_cpu(cmd.tx_data_timeout)); | |
e811ada7 | 769 | } |
175a70b7 AB |
770 | if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) |
771 | pos += scnprintf(buf+pos, bufsz-pos, | |
772 | "lprx_rssi_threshold = %d\n", | |
773 | cmd.lprx_rssi_threshold); | |
774 | ||
775 | if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) | |
776 | return pos; | |
777 | ||
778 | pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n", | |
779 | le32_to_cpu(cmd.rx_data_timeout_uapsd)); | |
780 | pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n", | |
781 | le32_to_cpu(cmd.tx_data_timeout_uapsd)); | |
782 | pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid); | |
783 | pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n", | |
784 | cmd.uapsd_ac_flags); | |
785 | pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n", | |
786 | cmd.uapsd_max_sp); | |
787 | pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n", | |
788 | cmd.heavy_tx_thld_packets); | |
789 | pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n", | |
790 | cmd.heavy_rx_thld_packets); | |
791 | pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n", | |
792 | cmd.heavy_tx_thld_percentage); | |
793 | pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n", | |
794 | cmd.heavy_rx_thld_percentage); | |
795 | pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n", | |
796 | (cmd.flags & | |
797 | cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ? | |
798 | 1 : 0); | |
799 | ||
800 | if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK))) | |
801 | return pos; | |
802 | ||
803 | pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n", | |
804 | cmd.snooze_interval); | |
805 | pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n", | |
806 | cmd.snooze_window); | |
807 | ||
e811ada7 AB |
808 | return pos; |
809 | } | |
810 | ||
b571a697 AB |
811 | void |
812 | iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, | |
813 | struct iwl_beacon_filter_cmd *cmd) | |
814 | { | |
815 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
816 | struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; | |
817 | ||
818 | if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA) | |
5dca7c24 | 819 | cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta); |
b571a697 AB |
820 | if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA) |
821 | cmd->bf_roaming_energy_delta = | |
5dca7c24 | 822 | cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta); |
b571a697 | 823 | if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE) |
5dca7c24 HG |
824 | cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state); |
825 | if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD) | |
826 | cmd->bf_temp_threshold = | |
827 | cpu_to_le32(dbgfs_bf->bf_temp_threshold); | |
828 | if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER) | |
829 | cmd->bf_temp_fast_filter = | |
830 | cpu_to_le32(dbgfs_bf->bf_temp_fast_filter); | |
831 | if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER) | |
832 | cmd->bf_temp_slow_filter = | |
833 | cpu_to_le32(dbgfs_bf->bf_temp_slow_filter); | |
b571a697 | 834 | if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG) |
5dca7c24 | 835 | cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag); |
b571a697 AB |
836 | if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER) |
837 | cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer); | |
838 | if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER) | |
839 | cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer); | |
840 | if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT) | |
5dca7c24 HG |
841 | cmd->ba_enable_beacon_abort = |
842 | cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort); | |
b571a697 AB |
843 | } |
844 | #endif | |
845 | ||
3dd37d05 EP |
846 | static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, |
847 | struct ieee80211_vif *vif, | |
848 | struct iwl_beacon_filter_cmd *cmd, | |
632fa0ea | 849 | u32 cmd_flags) |
7df15b1e HG |
850 | { |
851 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
071d4990 | 852 | int ret; |
7df15b1e | 853 | |
fa7b2e7f | 854 | if (mvmvif != mvm->bf_allowed_vif || !vif->bss_conf.dtim_period || |
7df15b1e HG |
855 | vif->type != NL80211_IFTYPE_STATION || vif->p2p) |
856 | return 0; | |
857 | ||
632fa0ea EG |
858 | iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd); |
859 | iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd); | |
3dd37d05 | 860 | ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags); |
071d4990 | 861 | |
632fa0ea | 862 | if (!ret) |
a20fd398 | 863 | mvmvif->bf_data.bf_enabled = true; |
071d4990 AB |
864 | |
865 | return ret; | |
7df15b1e HG |
866 | } |
867 | ||
3dd37d05 EP |
868 | int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, |
869 | struct ieee80211_vif *vif, | |
870 | u32 flags) | |
871 | { | |
872 | struct iwl_beacon_filter_cmd cmd = { | |
873 | IWL_BF_CMD_CONFIG_DEFAULTS, | |
874 | .bf_enable_beacon_filter = cpu_to_le32(1), | |
875 | }; | |
876 | ||
632fa0ea | 877 | return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags); |
3dd37d05 EP |
878 | } |
879 | ||
0db056d3 SS |
880 | static int _iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, |
881 | struct ieee80211_vif *vif, | |
632fa0ea | 882 | u32 flags) |
7df15b1e | 883 | { |
071d4990 AB |
884 | struct iwl_beacon_filter_cmd cmd = {}; |
885 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
886 | int ret; | |
7df15b1e | 887 | |
73e5f2c5 | 888 | if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) |
7df15b1e HG |
889 | return 0; |
890 | ||
3dd37d05 | 891 | ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags); |
071d4990 | 892 | |
632fa0ea | 893 | if (!ret) |
a20fd398 | 894 | mvmvif->bf_data.bf_enabled = false; |
071d4990 AB |
895 | |
896 | return ret; | |
7df15b1e | 897 | } |
e811ada7 | 898 | |
0db056d3 SS |
899 | int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, |
900 | struct ieee80211_vif *vif, | |
901 | u32 flags) | |
902 | { | |
632fa0ea | 903 | return _iwl_mvm_disable_beacon_filter(mvm, vif, flags); |
0db056d3 SS |
904 | } |
905 | ||
2533edce | 906 | static int iwl_mvm_power_set_ps(struct iwl_mvm *mvm) |
13b72322 | 907 | { |
128aa948 | 908 | bool disable_ps; |
13b72322 EG |
909 | int ret; |
910 | ||
13b72322 | 911 | /* disable PS if CAM */ |
b1873300 | 912 | disable_ps = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM); |
2533edce LC |
913 | /* ...or if any of the vifs require PS to be off */ |
914 | ieee80211_iterate_active_interfaces_atomic(mvm->hw, | |
915 | IEEE80211_IFACE_ITER_NORMAL, | |
916 | iwl_mvm_power_ps_disabled_iterator, | |
917 | &disable_ps); | |
b1873300 LC |
918 | |
919 | /* update device power state if it has changed */ | |
920 | if (mvm->ps_disabled != disable_ps) { | |
921 | bool old_ps_disabled = mvm->ps_disabled; | |
922 | ||
923 | mvm->ps_disabled = disable_ps; | |
924 | ret = iwl_mvm_power_update_device(mvm); | |
925 | if (ret) { | |
926 | mvm->ps_disabled = old_ps_disabled; | |
927 | return ret; | |
13b72322 EG |
928 | } |
929 | } | |
930 | ||
128aa948 LC |
931 | return 0; |
932 | } | |
933 | ||
ef9203d2 | 934 | static int iwl_mvm_power_set_ba(struct iwl_mvm *mvm, |
8812182e | 935 | struct ieee80211_vif *vif) |
128aa948 | 936 | { |
8812182e | 937 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
416ec4f3 AA |
938 | struct iwl_beacon_filter_cmd cmd = { |
939 | IWL_BF_CMD_CONFIG_DEFAULTS, | |
940 | .bf_enable_beacon_filter = cpu_to_le32(1), | |
941 | }; | |
ef9203d2 | 942 | |
8812182e | 943 | if (!mvmvif->bf_data.bf_enabled) |
ef9203d2 LC |
944 | return 0; |
945 | ||
702e975d | 946 | if (mvm->fwrt.cur_fw_img == IWL_UCODE_WOWLAN) |
416ec4f3 AA |
947 | cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); |
948 | ||
949 | mvmvif->bf_data.ba_enabled = !(!mvmvif->pm_enabled || | |
950 | mvm->ps_disabled || | |
951 | !vif->bss_conf.ps || | |
952 | iwl_mvm_vif_low_latency(mvmvif)); | |
ef9203d2 | 953 | |
632fa0ea | 954 | return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0); |
ef9203d2 LC |
955 | } |
956 | ||
957 | int iwl_mvm_power_update_ps(struct iwl_mvm *mvm) | |
958 | { | |
959 | struct iwl_power_vifs vifs = { | |
960 | .mvm = mvm, | |
961 | }; | |
962 | int ret; | |
963 | ||
964 | lockdep_assert_held(&mvm->mutex); | |
965 | ||
966 | /* get vifs info */ | |
967 | ieee80211_iterate_active_interfaces_atomic(mvm->hw, | |
968 | IEEE80211_IFACE_ITER_NORMAL, | |
969 | iwl_mvm_power_get_vifs_iterator, &vifs); | |
970 | ||
2533edce | 971 | ret = iwl_mvm_power_set_ps(mvm); |
ef9203d2 LC |
972 | if (ret) |
973 | return ret; | |
974 | ||
8812182e AA |
975 | if (vifs.bss_vif) |
976 | return iwl_mvm_power_set_ba(mvm, vifs.bss_vif); | |
977 | ||
978 | return 0; | |
ef9203d2 LC |
979 | } |
980 | ||
981 | int iwl_mvm_power_update_mac(struct iwl_mvm *mvm) | |
982 | { | |
128aa948 LC |
983 | struct iwl_power_vifs vifs = { |
984 | .mvm = mvm, | |
985 | }; | |
128aa948 LC |
986 | int ret; |
987 | ||
988 | lockdep_assert_held(&mvm->mutex); | |
989 | ||
990 | /* get vifs info */ | |
991 | ieee80211_iterate_active_interfaces_atomic(mvm->hw, | |
992 | IEEE80211_IFACE_ITER_NORMAL, | |
993 | iwl_mvm_power_get_vifs_iterator, &vifs); | |
994 | ||
995 | iwl_mvm_power_set_pm(mvm, &vifs); | |
996 | ||
2533edce | 997 | ret = iwl_mvm_power_set_ps(mvm); |
128aa948 LC |
998 | if (ret) |
999 | return ret; | |
1000 | ||
13b72322 EG |
1001 | if (vifs.bss_vif) { |
1002 | ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif); | |
1003 | if (ret) | |
1004 | return ret; | |
1005 | } | |
1006 | ||
1007 | if (vifs.p2p_vif) { | |
1008 | ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif); | |
1009 | if (ret) | |
1010 | return ret; | |
1011 | } | |
1012 | ||
8812182e AA |
1013 | if (vifs.bss_vif) |
1014 | return iwl_mvm_power_set_ba(mvm, vifs.bss_vif); | |
1015 | ||
1016 | return 0; | |
13b72322 | 1017 | } |