Commit | Line | Data |
---|---|---|
1ab26632 MK |
1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | /* | |
3 | * Copyright (C) 2022 Intel Corporation | |
4 | */ | |
5 | #include "mvm.h" | |
6 | ||
7 | static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw, | |
8 | struct ieee80211_vif *vif) | |
9 | { | |
10 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
11 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
12 | int ret; | |
13 | ||
14 | mutex_lock(&mvm->mutex); | |
15 | ||
16 | /* Common for MLD and non-MLD API */ | |
17 | if (iwl_mvm_mac_add_interface_common(mvm, hw, vif, &ret)) | |
18 | goto out_unlock; | |
19 | ||
20 | ret = iwl_mvm_mld_mac_ctxt_add(mvm, vif); | |
21 | if (ret) | |
22 | goto out_unlock; | |
23 | ||
24 | ret = iwl_mvm_power_update_mac(mvm); | |
25 | if (ret) | |
26 | goto out_remove_mac; | |
27 | ||
28 | /* beacon filtering */ | |
29 | ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); | |
30 | if (ret) | |
31 | goto out_remove_mac; | |
32 | ||
33 | if (!mvm->bf_allowed_vif && | |
34 | vif->type == NL80211_IFTYPE_STATION && !vif->p2p) { | |
35 | mvm->bf_allowed_vif = mvmvif; | |
36 | vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | | |
37 | IEEE80211_VIF_SUPPORTS_CQM_RSSI; | |
38 | } | |
39 | ||
40 | /* | |
41 | * P2P_DEVICE interface does not have a channel context assigned to it, | |
42 | * so a dedicated PHY context is allocated to it and the corresponding | |
43 | * MAC context is bound to it at this stage. | |
44 | */ | |
45 | if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { | |
650cadb7 GG |
46 | mvmvif->deflink.phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); |
47 | if (!mvmvif->deflink.phy_ctxt) { | |
1ab26632 MK |
48 | ret = -ENOSPC; |
49 | goto out_free_bf; | |
50 | } | |
51 | ||
650cadb7 | 52 | iwl_mvm_phy_ctxt_ref(mvm, mvmvif->deflink.phy_ctxt); |
1ab26632 MK |
53 | ret = iwl_mvm_add_link(mvm, vif); |
54 | if (ret) | |
55 | goto out_unref_phy; | |
56 | ||
57 | ret = iwl_mvm_link_changed(mvm, vif, | |
58 | LINK_CONTEXT_MODIFY_ACTIVE | | |
59 | LINK_CONTEXT_MODIFY_RATES_INFO, | |
60 | true); | |
61 | if (ret) | |
62 | goto out_remove_link; | |
63 | ||
64 | ret = iwl_mvm_mld_add_bcast_sta(mvm, vif); | |
65 | if (ret) | |
66 | goto out_remove_link; | |
67 | ||
68 | /* Save a pointer to p2p device vif, so it can later be used to | |
69 | * update the p2p device MAC when a GO is started/stopped | |
70 | */ | |
71 | mvm->p2p_device_vif = vif; | |
72 | } | |
73 | ||
74 | iwl_mvm_tcm_add_vif(mvm, vif); | |
75 | INIT_DELAYED_WORK(&mvmvif->csa_work, | |
76 | iwl_mvm_channel_switch_disconnect_wk); | |
77 | ||
78 | if (vif->type == NL80211_IFTYPE_MONITOR) { | |
79 | mvm->monitor_on = true; | |
80 | ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); | |
81 | } | |
82 | ||
83 | iwl_mvm_vif_dbgfs_register(mvm, vif); | |
84 | ||
85 | if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && | |
86 | vif->type == NL80211_IFTYPE_STATION && !vif->p2p && | |
87 | !mvm->csme_vif && mvm->mei_registered) { | |
88 | iwl_mei_set_nic_info(vif->addr, mvm->nvm_data->hw_addr); | |
89 | iwl_mei_set_netdev(ieee80211_vif_to_wdev(vif)->netdev); | |
90 | mvm->csme_vif = vif; | |
91 | } | |
92 | ||
93 | goto out_unlock; | |
94 | ||
95 | out_remove_link: | |
96 | /* Link needs to be deactivated before removal */ | |
97 | iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, false); | |
98 | iwl_mvm_remove_link(mvm, vif); | |
99 | out_unref_phy: | |
650cadb7 | 100 | iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); |
1ab26632 MK |
101 | out_free_bf: |
102 | if (mvm->bf_allowed_vif == mvmvif) { | |
103 | mvm->bf_allowed_vif = NULL; | |
104 | vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | | |
105 | IEEE80211_VIF_SUPPORTS_CQM_RSSI); | |
106 | } | |
107 | out_remove_mac: | |
650cadb7 GG |
108 | mvmvif->deflink.phy_ctxt = NULL; |
109 | mvmvif->link[0] = NULL; | |
1ab26632 MK |
110 | iwl_mvm_mld_mac_ctxt_remove(mvm, vif); |
111 | out_unlock: | |
112 | mutex_unlock(&mvm->mutex); | |
113 | ||
114 | return ret; | |
115 | } | |
116 | ||
60efeca1 MK |
117 | static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw, |
118 | struct ieee80211_vif *vif) | |
119 | { | |
120 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
121 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
122 | ||
123 | if (iwl_mvm_mac_remove_interface_common(hw, vif)) | |
124 | goto out; | |
125 | ||
126 | if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { | |
127 | mvm->p2p_device_vif = NULL; | |
128 | iwl_mvm_mld_rm_bcast_sta(mvm, vif); | |
129 | /* Link needs to be deactivated before removal */ | |
130 | iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, | |
131 | false); | |
132 | iwl_mvm_remove_link(mvm, vif); | |
650cadb7 GG |
133 | iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); |
134 | mvmvif->deflink.phy_ctxt = NULL; | |
60efeca1 MK |
135 | } |
136 | ||
137 | iwl_mvm_mld_mac_ctxt_remove(mvm, vif); | |
138 | ||
139 | RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL); | |
140 | ||
141 | if (vif->type == NL80211_IFTYPE_MONITOR) { | |
142 | mvm->monitor_on = false; | |
143 | __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags); | |
144 | } | |
145 | ||
146 | out: | |
147 | mutex_unlock(&mvm->mutex); | |
148 | } | |
149 | ||
50e81437 MK |
150 | static int __iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, |
151 | struct ieee80211_vif *vif, | |
152 | struct ieee80211_chanctx_conf *ctx, | |
153 | bool switching_chanctx) | |
154 | { | |
155 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
156 | int ret; | |
157 | ||
158 | if (__iwl_mvm_assign_vif_chanctx_common(mvm, vif, ctx, | |
159 | switching_chanctx, &ret)) | |
160 | goto out; | |
161 | ||
162 | ret = iwl_mvm_add_link(mvm, vif); | |
163 | if (ret) | |
164 | goto out; | |
165 | ret = iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, | |
166 | true); | |
167 | if (ret) | |
168 | goto out_remove_link; | |
169 | ||
170 | /* | |
171 | * Power state must be updated before quotas, | |
172 | * otherwise fw will complain. | |
173 | */ | |
174 | iwl_mvm_power_update_mac(mvm); | |
175 | ||
176 | if (vif->type == NL80211_IFTYPE_MONITOR) { | |
177 | ret = iwl_mvm_mld_add_snif_sta(mvm, vif); | |
178 | if (ret) | |
179 | goto out_remove_link; | |
180 | } | |
181 | ||
182 | goto out; | |
183 | ||
184 | out_remove_link: | |
185 | /* Link needs to be deactivated before removal */ | |
186 | iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, false); | |
187 | iwl_mvm_remove_link(mvm, vif); | |
188 | iwl_mvm_power_update_mac(mvm); | |
189 | out: | |
190 | if (ret) | |
650cadb7 | 191 | mvmvif->deflink.phy_ctxt = NULL; |
50e81437 MK |
192 | return ret; |
193 | } | |
194 | ||
195 | static int iwl_mvm_mld_assign_vif_chanctx(struct ieee80211_hw *hw, | |
196 | struct ieee80211_vif *vif, | |
197 | struct ieee80211_bss_conf *link_conf, | |
198 | struct ieee80211_chanctx_conf *ctx) | |
199 | { | |
200 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
201 | int ret; | |
202 | ||
203 | mutex_lock(&mvm->mutex); | |
204 | ret = __iwl_mvm_mld_assign_vif_chanctx(mvm, vif, ctx, false); | |
205 | mutex_unlock(&mvm->mutex); | |
206 | ||
207 | return ret; | |
208 | } | |
6f71e90e MK |
209 | |
210 | static void __iwl_mvm_mld_unassign_vif_chanctx(struct iwl_mvm *mvm, | |
211 | struct ieee80211_vif *vif, | |
212 | struct ieee80211_chanctx_conf *ctx, | |
213 | bool switching_chanctx) | |
214 | { | |
215 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
216 | ||
217 | if (__iwl_mvm_unassign_vif_chanctx_common(mvm, vif, switching_chanctx)) | |
218 | goto out; | |
219 | ||
220 | if (vif->type == NL80211_IFTYPE_MONITOR) | |
221 | iwl_mvm_mld_rm_snif_sta(mvm, vif); | |
222 | ||
6f71e90e MK |
223 | /* Link needs to be deactivated before removal */ |
224 | iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, false); | |
225 | iwl_mvm_remove_link(mvm, vif); | |
226 | ||
227 | out: | |
228 | if (switching_chanctx) | |
229 | return; | |
650cadb7 | 230 | mvmvif->deflink.phy_ctxt = NULL; |
6f71e90e MK |
231 | iwl_mvm_power_update_mac(mvm); |
232 | } | |
233 | ||
234 | static void iwl_mvm_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, | |
235 | struct ieee80211_vif *vif, | |
236 | struct ieee80211_bss_conf *link_conf, | |
237 | struct ieee80211_chanctx_conf *ctx) | |
238 | { | |
239 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
240 | ||
241 | mutex_lock(&mvm->mutex); | |
242 | __iwl_mvm_mld_unassign_vif_chanctx(mvm, vif, ctx, false); | |
243 | mutex_unlock(&mvm->mutex); | |
244 | } | |
f947b62c MK |
245 | |
246 | static int iwl_mvm_mld_start_ap_ibss(struct ieee80211_hw *hw, | |
cbce62a3 MK |
247 | struct ieee80211_vif *vif, |
248 | struct ieee80211_bss_conf *link_conf) | |
f947b62c MK |
249 | { |
250 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
251 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
252 | int ret; | |
253 | ||
254 | mutex_lock(&mvm->mutex); | |
255 | ||
256 | /* Send the beacon template */ | |
257 | ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif); | |
258 | if (ret) | |
259 | goto out_unlock; | |
260 | ||
261 | /* No need to re-calculate the tsf_is, as it was offloaded */ | |
262 | ||
263 | /* Add the mac context */ | |
264 | ret = iwl_mvm_mld_mac_ctxt_add(mvm, vif); | |
265 | if (ret) | |
266 | goto out_unlock; | |
267 | ||
268 | /* Add link and activate it */ | |
269 | ret = iwl_mvm_add_link(mvm, vif); | |
270 | if (ret) | |
271 | goto out_remove_mac; | |
272 | ||
273 | ret = iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, | |
274 | true); | |
275 | if (ret) | |
276 | goto out_remove_link; | |
277 | ||
278 | ret = iwl_mvm_mld_add_mcast_sta(mvm, vif); | |
279 | if (ret) | |
280 | goto out_remove_link; | |
281 | ||
282 | /* Send the bcast station. At this stage the TBTT and DTIM time | |
283 | * events are added and applied to the scheduler | |
284 | */ | |
285 | ret = iwl_mvm_mld_add_bcast_sta(mvm, vif); | |
286 | if (ret) | |
287 | goto out_rm_mcast; | |
288 | ||
289 | if (iwl_mvm_start_ap_ibss_common(hw, vif, &ret)) | |
290 | goto out_failed; | |
291 | ||
292 | /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ | |
293 | if (vif->p2p && mvm->p2p_device_vif) | |
294 | iwl_mvm_mld_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); | |
295 | ||
296 | iwl_mvm_bt_coex_vif_change(mvm); | |
297 | ||
298 | /* we don't support TDLS during DCM */ | |
299 | if (iwl_mvm_phy_ctx_count(mvm) > 1) | |
300 | iwl_mvm_teardown_tdls_peers(mvm); | |
301 | ||
302 | iwl_mvm_ftm_restart_responder(mvm, vif); | |
303 | ||
304 | goto out_unlock; | |
305 | ||
306 | out_failed: | |
307 | iwl_mvm_power_update_mac(mvm); | |
308 | mvmvif->ap_ibss_active = false; | |
309 | iwl_mvm_mld_rm_bcast_sta(mvm, vif); | |
310 | out_rm_mcast: | |
311 | iwl_mvm_mld_rm_mcast_sta(mvm, vif); | |
312 | out_remove_link: | |
313 | /* Link needs to be deactivated before removal */ | |
314 | iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, false); | |
315 | iwl_mvm_remove_link(mvm, vif); | |
316 | out_remove_mac: | |
317 | iwl_mvm_mld_mac_ctxt_remove(mvm, vif); | |
318 | out_unlock: | |
319 | mutex_unlock(&mvm->mutex); | |
320 | return ret; | |
321 | } | |
322 | ||
cbce62a3 MK |
323 | static int iwl_mvm_mld_start_ap(struct ieee80211_hw *hw, |
324 | struct ieee80211_vif *vif, | |
325 | struct ieee80211_bss_conf *link_conf) | |
326 | { | |
327 | return iwl_mvm_mld_start_ap_ibss(hw, vif, link_conf); | |
328 | } | |
329 | ||
330 | static int iwl_mvm_mld_start_ibss(struct ieee80211_hw *hw, | |
331 | struct ieee80211_vif *vif) | |
332 | { | |
333 | return iwl_mvm_mld_start_ap_ibss(hw, vif, &vif->bss_conf); | |
334 | } | |
335 | ||
fd1a54c1 | 336 | static void iwl_mvm_mld_stop_ap_ibss(struct ieee80211_hw *hw, |
cbce62a3 MK |
337 | struct ieee80211_vif *vif, |
338 | struct ieee80211_bss_conf *link_conf) | |
fd1a54c1 MK |
339 | { |
340 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
341 | ||
342 | mutex_lock(&mvm->mutex); | |
343 | ||
344 | iwl_mvm_stop_ap_ibss_common(mvm, vif); | |
345 | ||
346 | /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ | |
347 | if (vif->p2p && mvm->p2p_device_vif) | |
348 | iwl_mvm_mld_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); | |
349 | ||
350 | iwl_mvm_ftm_responder_clear(mvm, vif); | |
351 | ||
352 | iwl_mvm_mld_rm_bcast_sta(mvm, vif); | |
353 | iwl_mvm_mld_rm_mcast_sta(mvm, vif); | |
354 | ||
355 | /* Link needs to be deactivated before removal */ | |
356 | iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, false); | |
357 | iwl_mvm_remove_link(mvm, vif); | |
358 | ||
359 | iwl_mvm_power_update_mac(mvm); | |
360 | ||
361 | iwl_mvm_mld_mac_ctxt_remove(mvm, vif); | |
362 | ||
363 | mutex_unlock(&mvm->mutex); | |
364 | } | |
365 | ||
cbce62a3 MK |
366 | static void iwl_mvm_mld_stop_ap(struct ieee80211_hw *hw, |
367 | struct ieee80211_vif *vif, | |
368 | struct ieee80211_bss_conf *link_conf) | |
369 | { | |
370 | iwl_mvm_mld_stop_ap_ibss(hw, vif, link_conf); | |
371 | } | |
372 | ||
373 | static void iwl_mvm_mld_stop_ibss(struct ieee80211_hw *hw, | |
374 | struct ieee80211_vif *vif) | |
375 | { | |
376 | iwl_mvm_mld_stop_ap_ibss(hw, vif, &vif->bss_conf); | |
377 | } | |
378 | ||
87f7e243 MK |
379 | static int iwl_mvm_mld_mac_sta_state(struct ieee80211_hw *hw, |
380 | struct ieee80211_vif *vif, | |
381 | struct ieee80211_sta *sta, | |
382 | enum ieee80211_sta_state old_state, | |
383 | enum ieee80211_sta_state new_state) | |
384 | { | |
385 | struct iwl_mvm_sta_state_ops callbacks = { | |
386 | .add_sta = iwl_mvm_mld_add_sta, | |
387 | .update_sta = iwl_mvm_mld_update_sta, | |
388 | .rm_sta = iwl_mvm_mld_rm_sta, | |
389 | .mac_ctxt_changed = iwl_mvm_mld_mac_ctxt_changed, | |
390 | }; | |
391 | ||
392 | return iwl_mvm_mac_sta_state_common(hw, vif, sta, old_state, new_state, | |
393 | &callbacks); | |
394 | } | |
395 | ||
660eba5a MK |
396 | static void |
397 | iwl_mvm_mld_bss_info_changed_station(struct iwl_mvm *mvm, | |
398 | struct ieee80211_vif *vif, | |
399 | struct ieee80211_bss_conf *bss_conf, | |
400 | u64 changes) | |
401 | { | |
402 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
403 | int ret; | |
404 | u32 link_changes = 0; | |
405 | bool has_he = vif->bss_conf.he_support && | |
406 | !iwlwifi_mod_params.disable_11ax; | |
407 | bool has_eht = vif->bss_conf.eht_support && | |
408 | !iwlwifi_mod_params.disable_11be; | |
409 | ||
410 | if (changes & BSS_CHANGED_ASSOC && vif->cfg.assoc && | |
411 | (has_he || has_eht)) { | |
412 | IWL_DEBUG_MAC80211(mvm, "Associated in HE mode\n"); | |
413 | link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; | |
414 | } | |
415 | ||
416 | /* Update MU EDCA params */ | |
417 | if (changes & BSS_CHANGED_QOS && vif->cfg.assoc && | |
418 | (has_he || has_eht)) | |
419 | link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS; | |
420 | ||
421 | /* Update EHT Puncturing info */ | |
422 | if (changes & BSS_CHANGED_EHT_PUNCTURING && vif->cfg.assoc && has_eht) | |
423 | link_changes |= LINK_CONTEXT_MODIFY_EHT_PARAMS; | |
424 | ||
425 | if (link_changes) { | |
426 | ret = iwl_mvm_link_changed(mvm, vif, link_changes, true); | |
427 | if (ret) | |
428 | IWL_ERR(mvm, "failed to update link\n"); | |
429 | } | |
430 | ||
431 | ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); | |
432 | if (ret) | |
433 | IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); | |
434 | ||
435 | memcpy(mvmvif->deflink.bssid, bss_conf->bssid, ETH_ALEN); | |
436 | mvmvif->associated = vif->cfg.assoc; | |
437 | ||
438 | if (changes & BSS_CHANGED_ASSOC) { | |
439 | if (vif->cfg.assoc) { | |
440 | /* clear statistics to get clean beacon counter */ | |
441 | iwl_mvm_request_statistics(mvm, true); | |
442 | memset(&mvmvif->deflink.beacon_stats, 0, | |
443 | sizeof(mvmvif->deflink.beacon_stats)); | |
444 | ||
445 | if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, | |
446 | &mvm->status) && | |
447 | !vif->bss_conf.dtim_period) { | |
448 | /* If we're not restarting and still haven't | |
449 | * heard a beacon (dtim period unknown) then | |
450 | * make sure we still have enough minimum time | |
451 | * remaining in the time event, since the auth | |
452 | * might actually have taken quite a while | |
453 | * (especially for SAE) and so the remaining | |
454 | * time could be small without us having heard | |
455 | * a beacon yet. | |
456 | */ | |
457 | iwl_mvm_protect_assoc(mvm, vif, 0); | |
458 | } | |
459 | ||
460 | iwl_mvm_sf_update(mvm, vif, false); | |
461 | iwl_mvm_power_vif_assoc(mvm, vif); | |
462 | if (vif->p2p) { | |
463 | iwl_mvm_update_smps(mvm, vif, | |
464 | IWL_MVM_SMPS_REQ_PROT, | |
465 | IEEE80211_SMPS_DYNAMIC); | |
466 | } | |
467 | } else if (mvmvif->deflink.ap_sta_id != IWL_MVM_INVALID_STA) { | |
468 | iwl_mvm_mei_host_disassociated(mvm); | |
469 | /* If update fails - SF might be running in associated | |
470 | * mode while disassociated - which is forbidden. | |
471 | */ | |
472 | ret = iwl_mvm_sf_update(mvm, vif, false); | |
473 | WARN_ONCE(ret && | |
474 | !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, | |
475 | &mvm->status), | |
476 | "Failed to update SF upon disassociation\n"); | |
477 | ||
478 | /* If we get an assert during the connection (after the | |
479 | * station has been added, but before the vif is set | |
480 | * to associated), mac80211 will re-add the station and | |
481 | * then configure the vif. Since the vif is not | |
482 | * associated, we would remove the station here and | |
483 | * this would fail the recovery. | |
484 | */ | |
485 | if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, | |
486 | &mvm->status)) { | |
487 | /* first remove remaining keys */ | |
488 | iwl_mvm_sec_key_remove_ap(mvm, vif); | |
489 | ||
490 | /* Remove AP station now that | |
491 | * the MAC is unassoc | |
492 | */ | |
493 | ret = iwl_mvm_mld_rm_sta_id(mvm, vif, | |
494 | mvmvif->deflink.ap_sta_id); | |
495 | if (ret) | |
496 | IWL_ERR(mvm, | |
497 | "failed to remove AP station\n"); | |
498 | ||
499 | mvmvif->deflink.ap_sta_id = IWL_MVM_INVALID_STA; | |
500 | } | |
501 | } | |
502 | ||
503 | iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes); | |
504 | } | |
505 | ||
506 | iwl_mvm_bss_info_changed_station_common(mvm, vif, changes); | |
507 | } | |
508 | ||
509 | static void | |
510 | iwl_mvm_mld_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, | |
511 | struct ieee80211_vif *vif, | |
512 | struct ieee80211_bss_conf *bss_conf, | |
513 | u64 changes) | |
514 | { | |
515 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
516 | u32 link_changes = LINK_CONTEXT_MODIFY_PROTECT_FLAGS | | |
517 | LINK_CONTEXT_MODIFY_QOS_PARAMS; | |
518 | ||
519 | /* Changes will be applied when the AP/IBSS is started */ | |
520 | if (!mvmvif->ap_ibss_active) | |
521 | return; | |
522 | ||
523 | if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT | | |
524 | BSS_CHANGED_BANDWIDTH | BSS_CHANGED_QOS) && | |
525 | iwl_mvm_link_changed(mvm, vif, link_changes, true)) | |
526 | IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); | |
527 | ||
528 | /* Need to send a new beacon template to the FW */ | |
529 | if (changes & BSS_CHANGED_BEACON && | |
530 | iwl_mvm_mac_ctxt_beacon_changed(mvm, vif)) | |
531 | IWL_WARN(mvm, "Failed updating beacon data\n"); | |
532 | ||
533 | if (changes & BSS_CHANGED_FTM_RESPONDER) { | |
534 | int ret = iwl_mvm_ftm_start_responder(mvm, vif); | |
535 | ||
536 | if (ret) | |
537 | IWL_WARN(mvm, "Failed to enable FTM responder (%d)\n", | |
538 | ret); | |
539 | } | |
540 | } | |
541 | ||
542 | static void iwl_mvm_mld_bss_info_changed(struct ieee80211_hw *hw, | |
543 | struct ieee80211_vif *vif, | |
544 | struct ieee80211_bss_conf *bss_conf, | |
545 | u64 changes) | |
546 | { | |
547 | struct iwl_mvm_bss_info_changed_ops callbacks = { | |
548 | .bss_info_changed_sta = iwl_mvm_mld_bss_info_changed_station, | |
549 | .bss_info_changed_ap_ibss = | |
550 | iwl_mvm_mld_bss_info_changed_ap_ibss, | |
551 | }; | |
552 | ||
553 | iwl_mvm_bss_info_changed_common(hw, vif, bss_conf, &callbacks, | |
554 | changes); | |
555 | } | |
556 | ||
557 | static int | |
558 | iwl_mvm_mld_switch_vif_chanctx(struct ieee80211_hw *hw, | |
559 | struct ieee80211_vif_chanctx_switch *vifs, | |
560 | int n_vifs, | |
561 | enum ieee80211_chanctx_switch_mode mode) | |
562 | { | |
563 | struct iwl_mvm_switch_vif_chanctx_ops ops = { | |
564 | .__assign_vif_chanctx = __iwl_mvm_mld_assign_vif_chanctx, | |
565 | .__unassign_vif_chanctx = __iwl_mvm_mld_unassign_vif_chanctx, | |
566 | }; | |
567 | ||
568 | return iwl_mvm_switch_vif_chanctx_common(hw, vifs, n_vifs, mode, &ops); | |
569 | } | |
570 | ||
571 | static void iwl_mvm_mld_config_iface_filter(struct ieee80211_hw *hw, | |
572 | struct ieee80211_vif *vif, | |
573 | unsigned int filter_flags, | |
574 | unsigned int changed_flags) | |
575 | { | |
576 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
577 | ||
578 | /* We support only filter for probe requests */ | |
579 | if (!(changed_flags & FIF_PROBE_REQ)) | |
580 | return; | |
581 | ||
582 | /* Supported only for p2p client interfaces */ | |
583 | if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc || | |
584 | !vif->p2p) | |
585 | return; | |
586 | ||
587 | mutex_lock(&mvm->mutex); | |
588 | iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); | |
589 | mutex_unlock(&mvm->mutex); | |
590 | } | |
591 | ||
592 | static int | |
593 | iwl_mvm_mld_mac_conf_tx(struct ieee80211_hw *hw, | |
594 | struct ieee80211_vif *vif, | |
595 | unsigned int link_id, u16 ac, | |
596 | const struct ieee80211_tx_queue_params *params) | |
597 | { | |
598 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
599 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
600 | ||
601 | mvmvif->deflink.queue_params[ac] = *params; | |
602 | ||
603 | /* No need to update right away, we'll get BSS_CHANGED_QOS | |
604 | * The exception is P2P_DEVICE interface which needs immediate update. | |
605 | */ | |
606 | if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { | |
607 | int ret; | |
608 | ||
609 | mutex_lock(&mvm->mutex); | |
610 | ret = iwl_mvm_link_changed(mvm, vif, | |
611 | LINK_CONTEXT_MODIFY_QOS_PARAMS, | |
612 | true); | |
613 | mutex_unlock(&mvm->mutex); | |
614 | return ret; | |
615 | } | |
616 | return 0; | |
617 | } | |
618 | ||
fe8b2ad3 MK |
619 | static int iwl_mvm_link_switch_phy_ctx(struct iwl_mvm *mvm, |
620 | struct ieee80211_vif *vif, | |
621 | struct iwl_mvm_phy_ctxt *new_phy_ctxt) | |
622 | { | |
623 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
624 | int ret = 0; | |
625 | ||
626 | lockdep_assert_held(&mvm->mutex); | |
627 | ||
628 | /* Inorder to change the phy_ctx of a link, the link needs to be | |
629 | * inactive. Therefore, first deactivate the link, then change its | |
630 | * phy_ctx, and then activate it again. | |
631 | */ | |
632 | ret = iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, false); | |
633 | if (WARN(ret, "Failed to deactivate link\n")) | |
634 | return ret; | |
635 | ||
636 | iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); | |
637 | ||
638 | mvmvif->deflink.phy_ctxt = new_phy_ctxt; | |
639 | ||
640 | ret = iwl_mvm_link_changed(mvm, vif, 0, false); | |
641 | if (WARN(ret, "Failed to deactivate link\n")) | |
642 | return ret; | |
643 | ||
644 | ret = iwl_mvm_link_changed(mvm, vif, LINK_CONTEXT_MODIFY_ACTIVE, true); | |
645 | WARN(ret, "Failed binding P2P_DEVICE\n"); | |
646 | return ret; | |
647 | } | |
648 | ||
649 | static int iwl_mvm_mld_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
650 | struct ieee80211_channel *channel, int duration, | |
651 | enum ieee80211_roc_type type) | |
652 | { | |
653 | struct iwl_mvm_roc_ops ops = { | |
654 | .add_aux_sta_for_hs20 = iwl_mvm_mld_add_aux_sta, | |
655 | .switch_phy_ctxt = iwl_mvm_link_switch_phy_ctx, | |
656 | }; | |
657 | ||
658 | return iwl_mvm_roc_common(hw, vif, channel, duration, type, &ops); | |
659 | } | |
1ab26632 | 660 | const struct ieee80211_ops iwl_mvm_mld_hw_ops = { |
cbce62a3 MK |
661 | .tx = iwl_mvm_mac_tx, |
662 | .wake_tx_queue = iwl_mvm_mac_wake_tx_queue, | |
663 | .ampdu_action = iwl_mvm_mac_ampdu_action, | |
664 | .get_antenna = iwl_mvm_op_get_antenna, | |
665 | .start = iwl_mvm_mac_start, | |
666 | .reconfig_complete = iwl_mvm_mac_reconfig_complete, | |
667 | .stop = iwl_mvm_mac_stop, | |
1ab26632 | 668 | .add_interface = iwl_mvm_mld_mac_add_interface, |
60efeca1 | 669 | .remove_interface = iwl_mvm_mld_mac_remove_interface, |
cbce62a3 MK |
670 | .config = iwl_mvm_mac_config, |
671 | .prepare_multicast = iwl_mvm_prepare_multicast, | |
672 | .configure_filter = iwl_mvm_configure_filter, | |
660eba5a | 673 | .config_iface_filter = iwl_mvm_mld_config_iface_filter, |
cbce62a3 MK |
674 | .bss_info_changed = iwl_mvm_mld_bss_info_changed, |
675 | .hw_scan = iwl_mvm_mac_hw_scan, | |
676 | .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan, | |
677 | .sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove, | |
678 | .sta_state = iwl_mvm_mld_mac_sta_state, | |
679 | .sta_notify = iwl_mvm_mac_sta_notify, | |
680 | .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, | |
681 | .release_buffered_frames = iwl_mvm_mac_release_buffered_frames, | |
682 | .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, | |
683 | .sta_rc_update = iwl_mvm_sta_rc_update, | |
684 | .conf_tx = iwl_mvm_mld_mac_conf_tx, | |
685 | .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, | |
686 | .mgd_complete_tx = iwl_mvm_mac_mgd_complete_tx, | |
687 | .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover, | |
688 | .flush = iwl_mvm_mac_flush, | |
689 | .sched_scan_start = iwl_mvm_mac_sched_scan_start, | |
690 | .sched_scan_stop = iwl_mvm_mac_sched_scan_stop, | |
691 | .set_key = iwl_mvm_mac_set_key, | |
692 | .update_tkip_key = iwl_mvm_mac_update_tkip_key, | |
fe8b2ad3 MK |
693 | .remain_on_channel = iwl_mvm_mld_roc, |
694 | .cancel_remain_on_channel = iwl_mvm_cancel_roc, | |
cbce62a3 MK |
695 | .add_chanctx = iwl_mvm_add_chanctx, |
696 | .remove_chanctx = iwl_mvm_remove_chanctx, | |
697 | .change_chanctx = iwl_mvm_change_chanctx, | |
50e81437 | 698 | .assign_vif_chanctx = iwl_mvm_mld_assign_vif_chanctx, |
6f71e90e | 699 | .unassign_vif_chanctx = iwl_mvm_mld_unassign_vif_chanctx, |
660eba5a | 700 | .switch_vif_chanctx = iwl_mvm_mld_switch_vif_chanctx, |
cbce62a3 MK |
701 | |
702 | .start_ap = iwl_mvm_mld_start_ap, | |
703 | .stop_ap = iwl_mvm_mld_stop_ap, | |
704 | .join_ibss = iwl_mvm_mld_start_ibss, | |
705 | .leave_ibss = iwl_mvm_mld_stop_ibss, | |
706 | ||
707 | .tx_last_beacon = iwl_mvm_tx_last_beacon, | |
708 | ||
709 | .set_tim = iwl_mvm_set_tim, | |
710 | ||
711 | .channel_switch = iwl_mvm_channel_switch, | |
712 | .pre_channel_switch = iwl_mvm_pre_channel_switch, | |
03117f30 | 713 | .post_channel_switch = iwl_mvm_post_channel_switch, |
cbce62a3 MK |
714 | .abort_channel_switch = iwl_mvm_abort_channel_switch, |
715 | .channel_switch_rx_beacon = iwl_mvm_channel_switch_rx_beacon, | |
716 | ||
717 | .tdls_channel_switch = iwl_mvm_tdls_channel_switch, | |
718 | .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch, | |
719 | .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch, | |
720 | ||
721 | .event_callback = iwl_mvm_mac_event_callback, | |
722 | ||
723 | .sync_rx_queues = iwl_mvm_sync_rx_queues, | |
724 | ||
725 | CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) | |
726 | ||
727 | #ifdef CONFIG_PM_SLEEP | |
728 | /* look at d3.c */ | |
729 | .suspend = iwl_mvm_suspend, | |
730 | .resume = iwl_mvm_resume, | |
731 | .set_wakeup = iwl_mvm_set_wakeup, | |
732 | .set_rekey_data = iwl_mvm_set_rekey_data, | |
733 | #if IS_ENABLED(CONFIG_IPV6) | |
734 | .ipv6_addr_change = iwl_mvm_ipv6_addr_change, | |
735 | #endif | |
736 | .set_default_unicast_key = iwl_mvm_set_default_unicast_key, | |
737 | #endif | |
738 | .get_survey = iwl_mvm_mac_get_survey, | |
739 | .sta_statistics = iwl_mvm_mac_sta_statistics, | |
740 | .get_ftm_responder_stats = iwl_mvm_mac_get_ftm_responder_stats, | |
741 | .start_pmsr = iwl_mvm_start_pmsr, | |
742 | .abort_pmsr = iwl_mvm_abort_pmsr, | |
743 | ||
744 | #ifdef CONFIG_IWLWIFI_DEBUGFS | |
745 | .sta_add_debugfs = iwl_mvm_sta_add_debugfs, | |
746 | #endif | |
1ab26632 | 747 | }; |