]>
Commit | Line | Data |
---|---|---|
1ab26632 MK |
1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | /* | |
33acbe6a | 3 | * Copyright (C) 2022-2023 Intel Corporation |
1ab26632 MK |
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 | ||
cb145863 | 16 | mvmvif->mvm = mvm; |
1ab26632 | 17 | |
cb145863 JB |
18 | /* Not much to do here. The stack will not allow interface |
19 | * types or combinations that we didn't advertise, so we | |
20 | * don't really have to check the types. | |
21 | */ | |
22 | ||
23 | /* make sure that beacon statistics don't go backwards with FW reset */ | |
24 | if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) | |
25 | mvmvif->deflink.beacon_stats.accu_num_beacons += | |
26 | mvmvif->deflink.beacon_stats.num_beacons; | |
27 | ||
28 | /* Allocate resources for the MAC context, and add it to the fw */ | |
29 | ret = iwl_mvm_mac_ctxt_init(mvm, vif); | |
1ab26632 MK |
30 | if (ret) |
31 | goto out_unlock; | |
32 | ||
cb145863 JB |
33 | rcu_assign_pointer(mvm->vif_id_to_mac[mvmvif->id], vif); |
34 | ||
35 | mvmvif->features |= hw->netdev_features; | |
36 | ||
bf976c81 | 37 | /* reset deflink MLO parameters */ |
d6f6b0d8 | 38 | mvmvif->deflink.fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; |
bf976c81 JB |
39 | mvmvif->deflink.active = 0; |
40 | /* the first link always points to the default one */ | |
cb145863 JB |
41 | mvmvif->link[0] = &mvmvif->deflink; |
42 | ||
43 | ret = iwl_mvm_mld_mac_ctxt_add(mvm, vif); | |
1ab26632 | 44 | if (ret) |
cb145863 | 45 | goto out_unlock; |
1ab26632 MK |
46 | |
47 | /* beacon filtering */ | |
48 | ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); | |
49 | if (ret) | |
50 | goto out_remove_mac; | |
51 | ||
52 | if (!mvm->bf_allowed_vif && | |
53 | vif->type == NL80211_IFTYPE_STATION && !vif->p2p) { | |
54 | mvm->bf_allowed_vif = mvmvif; | |
55 | vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | | |
56 | IEEE80211_VIF_SUPPORTS_CQM_RSSI; | |
57 | } | |
58 | ||
59 | /* | |
60 | * P2P_DEVICE interface does not have a channel context assigned to it, | |
61 | * so a dedicated PHY context is allocated to it and the corresponding | |
62 | * MAC context is bound to it at this stage. | |
63 | */ | |
64 | if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { | |
650cadb7 GG |
65 | mvmvif->deflink.phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); |
66 | if (!mvmvif->deflink.phy_ctxt) { | |
1ab26632 MK |
67 | ret = -ENOSPC; |
68 | goto out_free_bf; | |
69 | } | |
70 | ||
650cadb7 | 71 | iwl_mvm_phy_ctxt_ref(mvm, mvmvif->deflink.phy_ctxt); |
cacc1d42 | 72 | ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); |
1ab26632 MK |
73 | if (ret) |
74 | goto out_unref_phy; | |
75 | ||
cacc1d42 GG |
76 | ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, |
77 | LINK_CONTEXT_MODIFY_ACTIVE | | |
78 | LINK_CONTEXT_MODIFY_RATES_INFO, | |
1ab26632 MK |
79 | true); |
80 | if (ret) | |
81 | goto out_remove_link; | |
82 | ||
62e0ccb2 | 83 | ret = iwl_mvm_mld_add_bcast_sta(mvm, vif, &vif->bss_conf); |
1ab26632 MK |
84 | if (ret) |
85 | goto out_remove_link; | |
86 | ||
87 | /* Save a pointer to p2p device vif, so it can later be used to | |
88 | * update the p2p device MAC when a GO is started/stopped | |
89 | */ | |
90 | mvm->p2p_device_vif = vif; | |
cb145863 | 91 | } else { |
cacc1d42 | 92 | ret = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); |
cb145863 JB |
93 | if (ret) |
94 | goto out_free_bf; | |
1ab26632 MK |
95 | } |
96 | ||
cb145863 JB |
97 | ret = iwl_mvm_power_update_mac(mvm); |
98 | if (ret) | |
99 | goto out_free_bf; | |
100 | ||
1ab26632 MK |
101 | iwl_mvm_tcm_add_vif(mvm, vif); |
102 | INIT_DELAYED_WORK(&mvmvif->csa_work, | |
103 | iwl_mvm_channel_switch_disconnect_wk); | |
104 | ||
105 | if (vif->type == NL80211_IFTYPE_MONITOR) { | |
106 | mvm->monitor_on = true; | |
107 | ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); | |
108 | } | |
109 | ||
110 | iwl_mvm_vif_dbgfs_register(mvm, vif); | |
111 | ||
112 | if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && | |
113 | vif->type == NL80211_IFTYPE_STATION && !vif->p2p && | |
114 | !mvm->csme_vif && mvm->mei_registered) { | |
115 | iwl_mei_set_nic_info(vif->addr, mvm->nvm_data->hw_addr); | |
116 | iwl_mei_set_netdev(ieee80211_vif_to_wdev(vif)->netdev); | |
117 | mvm->csme_vif = vif; | |
118 | } | |
119 | ||
120 | goto out_unlock; | |
121 | ||
122 | out_remove_link: | |
bf976c81 | 123 | iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); |
1ab26632 | 124 | out_unref_phy: |
650cadb7 | 125 | iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); |
1ab26632 MK |
126 | out_free_bf: |
127 | if (mvm->bf_allowed_vif == mvmvif) { | |
128 | mvm->bf_allowed_vif = NULL; | |
129 | vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | | |
130 | IEEE80211_VIF_SUPPORTS_CQM_RSSI); | |
131 | } | |
132 | out_remove_mac: | |
650cadb7 GG |
133 | mvmvif->deflink.phy_ctxt = NULL; |
134 | mvmvif->link[0] = NULL; | |
1ab26632 MK |
135 | iwl_mvm_mld_mac_ctxt_remove(mvm, vif); |
136 | out_unlock: | |
137 | mutex_unlock(&mvm->mutex); | |
138 | ||
139 | return ret; | |
140 | } | |
141 | ||
60efeca1 MK |
142 | static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw, |
143 | struct ieee80211_vif *vif) | |
144 | { | |
145 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
146 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
cb145863 | 147 | struct iwl_probe_resp_data *probe_data; |
60efeca1 | 148 | |
cb145863 JB |
149 | iwl_mvm_prepare_mac_removal(mvm, vif); |
150 | ||
151 | if (!(vif->type == NL80211_IFTYPE_AP || | |
152 | vif->type == NL80211_IFTYPE_ADHOC)) | |
153 | iwl_mvm_tcm_rm_vif(mvm, vif); | |
154 | ||
155 | mutex_lock(&mvm->mutex); | |
156 | ||
157 | if (vif == mvm->csme_vif) { | |
158 | iwl_mei_set_netdev(NULL); | |
159 | mvm->csme_vif = NULL; | |
160 | } | |
161 | ||
cb145863 JB |
162 | if (mvm->bf_allowed_vif == mvmvif) { |
163 | mvm->bf_allowed_vif = NULL; | |
164 | vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | | |
165 | IEEE80211_VIF_SUPPORTS_CQM_RSSI); | |
166 | } | |
167 | ||
168 | if (vif->bss_conf.ftm_responder) | |
169 | memset(&mvm->ftm_resp_stats, 0, sizeof(mvm->ftm_resp_stats)); | |
170 | ||
171 | iwl_mvm_vif_dbgfs_clean(mvm, vif); | |
172 | ||
173 | /* For AP/GO interface, the tear down of the resources allocated to the | |
174 | * interface is be handled as part of the stop_ap flow. | |
175 | */ | |
176 | if (vif->type == NL80211_IFTYPE_AP || | |
177 | vif->type == NL80211_IFTYPE_ADHOC) { | |
178 | #ifdef CONFIG_NL80211_TESTMODE | |
179 | if (vif == mvm->noa_vif) { | |
180 | mvm->noa_vif = NULL; | |
181 | mvm->noa_duration = 0; | |
182 | } | |
183 | #endif | |
184 | } | |
185 | ||
186 | iwl_mvm_power_update_mac(mvm); | |
60efeca1 MK |
187 | |
188 | if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { | |
189 | mvm->p2p_device_vif = NULL; | |
62e0ccb2 GG |
190 | |
191 | /* P2P device uses only one link */ | |
192 | iwl_mvm_mld_rm_bcast_sta(mvm, vif, &vif->bss_conf); | |
bf976c81 | 193 | iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); |
650cadb7 GG |
194 | iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); |
195 | mvmvif->deflink.phy_ctxt = NULL; | |
cb145863 | 196 | } else { |
bf976c81 | 197 | iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); |
60efeca1 MK |
198 | } |
199 | ||
200 | iwl_mvm_mld_mac_ctxt_remove(mvm, vif); | |
201 | ||
202 | RCU_INIT_POINTER(mvm->vif_id_to_mac[mvmvif->id], NULL); | |
203 | ||
8ca86d61 JB |
204 | probe_data = rcu_dereference_protected(mvmvif->deflink.probe_resp_data, |
205 | lockdep_is_held(&mvm->mutex)); | |
206 | RCU_INIT_POINTER(mvmvif->deflink.probe_resp_data, NULL); | |
207 | if (probe_data) | |
208 | kfree_rcu(probe_data, rcu_head); | |
209 | ||
60efeca1 MK |
210 | if (vif->type == NL80211_IFTYPE_MONITOR) { |
211 | mvm->monitor_on = false; | |
212 | __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags); | |
213 | } | |
214 | ||
60efeca1 MK |
215 | mutex_unlock(&mvm->mutex); |
216 | } | |
217 | ||
12bacfc2 MK |
218 | static unsigned int iwl_mvm_mld_count_active_links(struct ieee80211_vif *vif) |
219 | { | |
220 | unsigned int n_active = 0; | |
221 | int i; | |
222 | ||
223 | for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { | |
224 | struct ieee80211_bss_conf *link_conf; | |
225 | ||
226 | link_conf = link_conf_dereference_protected(vif, i); | |
227 | if (link_conf && | |
228 | rcu_access_pointer(link_conf->chanctx_conf)) | |
229 | n_active++; | |
230 | } | |
231 | ||
232 | return n_active; | |
233 | } | |
234 | ||
235 | static int iwl_mvm_esr_mode_active(struct iwl_mvm *mvm, | |
236 | struct ieee80211_vif *vif) | |
237 | { | |
238 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
239 | int link_id, ret = 0; | |
240 | ||
241 | mvmvif->esr_active = true; | |
242 | ||
243 | /* Disable SMPS overrideing by user */ | |
244 | vif->driver_flags |= IEEE80211_VIF_DISABLE_SMPS_OVERRIDE; | |
245 | ||
246 | iwl_mvm_update_smps_on_active_links(mvm, vif, IWL_MVM_SMPS_REQ_FW, | |
247 | IEEE80211_SMPS_OFF); | |
248 | ||
249 | for_each_mvm_vif_valid_link(mvmvif, link_id) { | |
250 | struct iwl_mvm_vif_link_info *link = mvmvif->link[link_id]; | |
251 | ||
252 | if (!link->phy_ctxt) | |
253 | continue; | |
254 | ||
255 | ret = iwl_mvm_phy_send_rlc(mvm, link->phy_ctxt, 2, 2); | |
256 | if (ret) | |
257 | break; | |
258 | ||
259 | link->phy_ctxt->rlc_disabled = true; | |
260 | } | |
261 | ||
262 | return ret; | |
263 | } | |
264 | ||
4263ac7f GG |
265 | static int |
266 | __iwl_mvm_mld_assign_vif_chanctx(struct iwl_mvm *mvm, | |
267 | struct ieee80211_vif *vif, | |
268 | struct ieee80211_bss_conf *link_conf, | |
269 | struct ieee80211_chanctx_conf *ctx, | |
270 | bool switching_chanctx) | |
50e81437 | 271 | { |
cb145863 JB |
272 | u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; |
273 | struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; | |
12bacfc2 | 274 | unsigned int n_active = iwl_mvm_mld_count_active_links(vif); |
50e81437 | 275 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
4263ac7f | 276 | unsigned int link_id = link_conf->link_id; |
50e81437 MK |
277 | int ret; |
278 | ||
12bacfc2 MK |
279 | /* if the assigned one was not counted yet, count it now */ |
280 | if (!rcu_access_pointer(link_conf->chanctx_conf)) | |
281 | n_active++; | |
282 | ||
283 | if (n_active > iwl_mvm_max_active_links(mvm, vif)) | |
284 | return -EOPNOTSUPP; | |
285 | ||
4263ac7f GG |
286 | if (WARN_ON_ONCE(!mvmvif->link[link_id])) |
287 | return -EINVAL; | |
288 | ||
ef36f1df GG |
289 | /* mac parameters such as HE support can change at this stage |
290 | * For sta, need first to configure correct state from drv_sta_state | |
291 | * and only after that update mac config. | |
292 | */ | |
293 | if (vif->type == NL80211_IFTYPE_AP) { | |
294 | ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); | |
295 | if (ret) { | |
296 | IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); | |
297 | return -EINVAL; | |
298 | } | |
299 | } | |
300 | ||
12bacfc2 MK |
301 | if (iwl_mvm_is_esr_supported(mvm->fwrt.trans) && n_active > 1) { |
302 | mvmvif->link[link_id]->listen_lmac = true; | |
303 | ret = iwl_mvm_esr_mode_active(mvm, vif); | |
304 | if (ret) { | |
305 | IWL_ERR(mvm, "failed to activate ESR mode (%d)\n", ret); | |
306 | return ret; | |
307 | } | |
308 | } | |
309 | ||
4263ac7f | 310 | mvmvif->link[link_id]->phy_ctxt = phy_ctxt; |
50e81437 | 311 | |
cb145863 JB |
312 | if (switching_chanctx) { |
313 | /* reactivate if we turned this off during channel switch */ | |
314 | if (vif->type == NL80211_IFTYPE_AP) | |
315 | mvmvif->ap_ibss_active = true; | |
786810de MK |
316 | } |
317 | ||
cb145863 | 318 | /* send it first with phy context ID */ |
4263ac7f | 319 | ret = iwl_mvm_link_changed(mvm, vif, link_conf, 0, false); |
cb145863 JB |
320 | if (ret) |
321 | goto out; | |
322 | ||
d2d0468f JB |
323 | /* Initialize rate control for the AP station, since we might be |
324 | * doing a link switch here - we cannot initialize it before since | |
325 | * this needs the phy context assigned (and in FW?), and we cannot | |
326 | * do it later because it needs to be initialized as soon as we're | |
327 | * able to TX on the link, i.e. when active. | |
328 | * | |
329 | * Firmware restart isn't quite correct yet for MLO, but we don't | |
330 | * need to do it in that case anyway since it will happen from the | |
331 | * normal station state callback. | |
332 | */ | |
333 | if (mvmvif->ap_sta && | |
334 | !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { | |
335 | struct ieee80211_link_sta *link_sta; | |
336 | ||
337 | rcu_read_lock(); | |
338 | link_sta = rcu_dereference(mvmvif->ap_sta->link[link_id]); | |
339 | ||
340 | if (!WARN_ON_ONCE(!link_sta)) | |
341 | iwl_mvm_rs_rate_init(mvm, vif, mvmvif->ap_sta, | |
342 | link_conf, link_sta, | |
343 | phy_ctxt->channel->band); | |
344 | rcu_read_unlock(); | |
345 | } | |
346 | ||
cb145863 | 347 | /* then activate */ |
4263ac7f | 348 | ret = iwl_mvm_link_changed(mvm, vif, link_conf, |
cacc1d42 | 349 | LINK_CONTEXT_MODIFY_ACTIVE | |
cea05a86 | 350 | LINK_CONTEXT_MODIFY_RATES_INFO, |
50e81437 MK |
351 | true); |
352 | if (ret) | |
cb145863 | 353 | goto out; |
50e81437 MK |
354 | |
355 | /* | |
356 | * Power state must be updated before quotas, | |
357 | * otherwise fw will complain. | |
358 | */ | |
359 | iwl_mvm_power_update_mac(mvm); | |
360 | ||
361 | if (vif->type == NL80211_IFTYPE_MONITOR) { | |
4263ac7f | 362 | ret = iwl_mvm_mld_add_snif_sta(mvm, vif, link_conf); |
50e81437 | 363 | if (ret) |
cb145863 | 364 | goto deactivate; |
50e81437 MK |
365 | } |
366 | ||
cb145863 | 367 | return 0; |
50e81437 | 368 | |
cb145863 | 369 | deactivate: |
4263ac7f GG |
370 | iwl_mvm_link_changed(mvm, vif, link_conf, LINK_CONTEXT_MODIFY_ACTIVE, |
371 | false); | |
50e81437 | 372 | out: |
4263ac7f | 373 | mvmvif->link[link_id]->phy_ctxt = NULL; |
cb145863 | 374 | iwl_mvm_power_update_mac(mvm); |
50e81437 MK |
375 | return ret; |
376 | } | |
377 | ||
378 | static int iwl_mvm_mld_assign_vif_chanctx(struct ieee80211_hw *hw, | |
379 | struct ieee80211_vif *vif, | |
380 | struct ieee80211_bss_conf *link_conf, | |
381 | struct ieee80211_chanctx_conf *ctx) | |
382 | { | |
383 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
384 | int ret; | |
385 | ||
386 | mutex_lock(&mvm->mutex); | |
4263ac7f | 387 | ret = __iwl_mvm_mld_assign_vif_chanctx(mvm, vif, link_conf, ctx, false); |
50e81437 MK |
388 | mutex_unlock(&mvm->mutex); |
389 | ||
390 | return ret; | |
391 | } | |
6f71e90e | 392 | |
12bacfc2 MK |
393 | static int iwl_mvm_esr_mode_inactive(struct iwl_mvm *mvm, |
394 | struct ieee80211_vif *vif) | |
395 | { | |
396 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
397 | struct ieee80211_bss_conf *link_conf; | |
398 | int link_id, ret = 0; | |
399 | ||
400 | mvmvif->esr_active = false; | |
401 | ||
402 | vif->driver_flags &= ~IEEE80211_VIF_DISABLE_SMPS_OVERRIDE; | |
403 | ||
404 | iwl_mvm_update_smps_on_active_links(mvm, vif, IWL_MVM_SMPS_REQ_FW, | |
405 | IEEE80211_SMPS_AUTOMATIC); | |
406 | ||
407 | for_each_vif_active_link(vif, link_conf, link_id) { | |
408 | struct ieee80211_chanctx_conf *chanctx_conf; | |
409 | struct iwl_mvm_phy_ctxt *phy_ctxt; | |
410 | u8 static_chains, dynamic_chains; | |
411 | ||
412 | mvmvif->link[link_id]->listen_lmac = false; | |
413 | ||
414 | rcu_read_lock(); | |
415 | ||
416 | chanctx_conf = rcu_dereference(link_conf->chanctx_conf); | |
417 | phy_ctxt = mvmvif->link[link_id]->phy_ctxt; | |
418 | ||
419 | if (!chanctx_conf || !phy_ctxt) { | |
420 | rcu_read_unlock(); | |
421 | continue; | |
422 | } | |
423 | ||
424 | phy_ctxt->rlc_disabled = false; | |
425 | static_chains = chanctx_conf->rx_chains_static; | |
426 | dynamic_chains = chanctx_conf->rx_chains_dynamic; | |
427 | ||
428 | rcu_read_unlock(); | |
429 | ||
430 | ret = iwl_mvm_phy_send_rlc(mvm, phy_ctxt, static_chains, | |
431 | dynamic_chains); | |
432 | if (ret) | |
433 | break; | |
434 | } | |
435 | ||
436 | return ret; | |
437 | } | |
438 | ||
4263ac7f GG |
439 | static void |
440 | __iwl_mvm_mld_unassign_vif_chanctx(struct iwl_mvm *mvm, | |
441 | struct ieee80211_vif *vif, | |
442 | struct ieee80211_bss_conf *link_conf, | |
443 | struct ieee80211_chanctx_conf *ctx, | |
444 | bool switching_chanctx) | |
12bacfc2 | 445 | |
6f71e90e MK |
446 | { |
447 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
12bacfc2 | 448 | unsigned int n_active = iwl_mvm_mld_count_active_links(vif); |
4263ac7f GG |
449 | unsigned int link_id = link_conf->link_id; |
450 | ||
451 | /* shouldn't happen, but verify link_id is valid before accessing */ | |
452 | if (WARN_ON_ONCE(!mvmvif->link[link_id])) | |
453 | return; | |
6f71e90e | 454 | |
cb145863 JB |
455 | if (vif->type == NL80211_IFTYPE_AP && switching_chanctx) { |
456 | mvmvif->csa_countdown = false; | |
457 | ||
458 | /* Set CS bit on all the stations */ | |
459 | iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true); | |
460 | ||
461 | /* Save blocked iface, the timeout is set on the next beacon */ | |
462 | rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif); | |
463 | ||
464 | mvmvif->ap_ibss_active = false; | |
465 | } | |
6f71e90e | 466 | |
12bacfc2 MK |
467 | if (iwl_mvm_is_esr_supported(mvm->fwrt.trans) && n_active > 1) { |
468 | int ret = iwl_mvm_esr_mode_inactive(mvm, vif); | |
469 | ||
470 | if (ret) | |
471 | IWL_ERR(mvm, "failed to deactivate ESR mode (%d)\n", | |
472 | ret); | |
473 | } | |
474 | ||
6f71e90e MK |
475 | if (vif->type == NL80211_IFTYPE_MONITOR) |
476 | iwl_mvm_mld_rm_snif_sta(mvm, vif); | |
477 | ||
4263ac7f | 478 | iwl_mvm_link_changed(mvm, vif, link_conf, |
cacc1d42 | 479 | LINK_CONTEXT_MODIFY_ACTIVE, false); |
cb145863 | 480 | |
6f71e90e MK |
481 | if (switching_chanctx) |
482 | return; | |
4263ac7f | 483 | mvmvif->link[link_id]->phy_ctxt = NULL; |
6f71e90e MK |
484 | iwl_mvm_power_update_mac(mvm); |
485 | } | |
486 | ||
487 | static void iwl_mvm_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, | |
488 | struct ieee80211_vif *vif, | |
489 | struct ieee80211_bss_conf *link_conf, | |
490 | struct ieee80211_chanctx_conf *ctx) | |
491 | { | |
492 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
493 | ||
494 | mutex_lock(&mvm->mutex); | |
4263ac7f | 495 | __iwl_mvm_mld_unassign_vif_chanctx(mvm, vif, link_conf, ctx, false); |
6f71e90e MK |
496 | mutex_unlock(&mvm->mutex); |
497 | } | |
f947b62c MK |
498 | |
499 | static int iwl_mvm_mld_start_ap_ibss(struct ieee80211_hw *hw, | |
cbce62a3 MK |
500 | struct ieee80211_vif *vif, |
501 | struct ieee80211_bss_conf *link_conf) | |
f947b62c MK |
502 | { |
503 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
504 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
505 | int ret; | |
506 | ||
507 | mutex_lock(&mvm->mutex); | |
36cf5377 GG |
508 | /* Send the beacon template */ |
509 | ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf); | |
510 | if (ret) | |
511 | goto out_unlock; | |
512 | ||
bf976c81 JB |
513 | /* the link should be already activated when assigning chan context */ |
514 | ret = iwl_mvm_link_changed(mvm, vif, link_conf, | |
515 | LINK_CONTEXT_MODIFY_ALL & | |
516 | ~LINK_CONTEXT_MODIFY_ACTIVE, | |
f947b62c MK |
517 | true); |
518 | if (ret) | |
cb145863 | 519 | goto out_unlock; |
f947b62c | 520 | |
62e0ccb2 | 521 | ret = iwl_mvm_mld_add_mcast_sta(mvm, vif, link_conf); |
f947b62c | 522 | if (ret) |
cb145863 | 523 | goto out_unlock; |
f947b62c MK |
524 | |
525 | /* Send the bcast station. At this stage the TBTT and DTIM time | |
526 | * events are added and applied to the scheduler | |
527 | */ | |
62e0ccb2 | 528 | ret = iwl_mvm_mld_add_bcast_sta(mvm, vif, link_conf); |
f947b62c MK |
529 | if (ret) |
530 | goto out_rm_mcast; | |
531 | ||
532 | if (iwl_mvm_start_ap_ibss_common(hw, vif, &ret)) | |
533 | goto out_failed; | |
534 | ||
535 | /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ | |
536 | if (vif->p2p && mvm->p2p_device_vif) | |
537 | iwl_mvm_mld_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); | |
538 | ||
539 | iwl_mvm_bt_coex_vif_change(mvm); | |
540 | ||
541 | /* we don't support TDLS during DCM */ | |
542 | if (iwl_mvm_phy_ctx_count(mvm) > 1) | |
543 | iwl_mvm_teardown_tdls_peers(mvm); | |
544 | ||
fd940de7 | 545 | iwl_mvm_ftm_restart_responder(mvm, vif, link_conf); |
f947b62c MK |
546 | |
547 | goto out_unlock; | |
548 | ||
549 | out_failed: | |
550 | iwl_mvm_power_update_mac(mvm); | |
551 | mvmvif->ap_ibss_active = false; | |
62e0ccb2 | 552 | iwl_mvm_mld_rm_bcast_sta(mvm, vif, link_conf); |
f947b62c | 553 | out_rm_mcast: |
62e0ccb2 | 554 | iwl_mvm_mld_rm_mcast_sta(mvm, vif, link_conf); |
f947b62c MK |
555 | out_unlock: |
556 | mutex_unlock(&mvm->mutex); | |
557 | return ret; | |
558 | } | |
559 | ||
cbce62a3 MK |
560 | static int iwl_mvm_mld_start_ap(struct ieee80211_hw *hw, |
561 | struct ieee80211_vif *vif, | |
562 | struct ieee80211_bss_conf *link_conf) | |
563 | { | |
564 | return iwl_mvm_mld_start_ap_ibss(hw, vif, link_conf); | |
565 | } | |
566 | ||
567 | static int iwl_mvm_mld_start_ibss(struct ieee80211_hw *hw, | |
568 | struct ieee80211_vif *vif) | |
569 | { | |
570 | return iwl_mvm_mld_start_ap_ibss(hw, vif, &vif->bss_conf); | |
571 | } | |
572 | ||
fd1a54c1 | 573 | static void iwl_mvm_mld_stop_ap_ibss(struct ieee80211_hw *hw, |
cbce62a3 MK |
574 | struct ieee80211_vif *vif, |
575 | struct ieee80211_bss_conf *link_conf) | |
fd1a54c1 MK |
576 | { |
577 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
578 | ||
579 | mutex_lock(&mvm->mutex); | |
580 | ||
581 | iwl_mvm_stop_ap_ibss_common(mvm, vif); | |
582 | ||
583 | /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ | |
584 | if (vif->p2p && mvm->p2p_device_vif) | |
585 | iwl_mvm_mld_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); | |
586 | ||
587 | iwl_mvm_ftm_responder_clear(mvm, vif); | |
588 | ||
62e0ccb2 GG |
589 | iwl_mvm_mld_rm_bcast_sta(mvm, vif, link_conf); |
590 | iwl_mvm_mld_rm_mcast_sta(mvm, vif, link_conf); | |
fd1a54c1 | 591 | |
fd1a54c1 | 592 | iwl_mvm_power_update_mac(mvm); |
fd1a54c1 MK |
593 | mutex_unlock(&mvm->mutex); |
594 | } | |
595 | ||
cbce62a3 MK |
596 | static void iwl_mvm_mld_stop_ap(struct ieee80211_hw *hw, |
597 | struct ieee80211_vif *vif, | |
598 | struct ieee80211_bss_conf *link_conf) | |
599 | { | |
600 | iwl_mvm_mld_stop_ap_ibss(hw, vif, link_conf); | |
601 | } | |
602 | ||
603 | static void iwl_mvm_mld_stop_ibss(struct ieee80211_hw *hw, | |
604 | struct ieee80211_vif *vif) | |
605 | { | |
606 | iwl_mvm_mld_stop_ap_ibss(hw, vif, &vif->bss_conf); | |
607 | } | |
608 | ||
87f7e243 MK |
609 | static int iwl_mvm_mld_mac_sta_state(struct ieee80211_hw *hw, |
610 | struct ieee80211_vif *vif, | |
611 | struct ieee80211_sta *sta, | |
612 | enum ieee80211_sta_state old_state, | |
613 | enum ieee80211_sta_state new_state) | |
614 | { | |
a2906ea6 | 615 | static const struct iwl_mvm_sta_state_ops callbacks = { |
87f7e243 MK |
616 | .add_sta = iwl_mvm_mld_add_sta, |
617 | .update_sta = iwl_mvm_mld_update_sta, | |
618 | .rm_sta = iwl_mvm_mld_rm_sta, | |
619 | .mac_ctxt_changed = iwl_mvm_mld_mac_ctxt_changed, | |
620 | }; | |
621 | ||
622 | return iwl_mvm_mac_sta_state_common(hw, vif, sta, old_state, new_state, | |
623 | &callbacks); | |
624 | } | |
625 | ||
660eba5a | 626 | static void |
22c58834 GG |
627 | iwl_mvm_mld_link_info_changed_station(struct iwl_mvm *mvm, |
628 | struct ieee80211_vif *vif, | |
629 | struct ieee80211_bss_conf *link_conf, | |
630 | u64 changes) | |
660eba5a MK |
631 | { |
632 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
22c58834 | 633 | bool has_he, has_eht; |
660eba5a | 634 | u32 link_changes = 0; |
22c58834 GG |
635 | int ret; |
636 | ||
637 | if (WARN_ON_ONCE(!mvmvif->link[link_conf->link_id])) | |
638 | return; | |
639 | ||
640 | has_he = link_conf->he_support && !iwlwifi_mod_params.disable_11ax; | |
641 | has_eht = link_conf->eht_support && !iwlwifi_mod_params.disable_11be; | |
660eba5a | 642 | |
22c58834 GG |
643 | /* Update EDCA params */ |
644 | if (changes & BSS_CHANGED_QOS && vif->cfg.assoc && link_conf->qos) | |
645 | link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS; | |
646 | ||
647 | if (changes & BSS_CHANGED_ERP_SLOT) | |
648 | link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO; | |
649 | ||
650 | if (vif->cfg.assoc && (has_he || has_eht)) { | |
660eba5a MK |
651 | IWL_DEBUG_MAC80211(mvm, "Associated in HE mode\n"); |
652 | link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; | |
653 | } | |
654 | ||
660eba5a MK |
655 | /* Update EHT Puncturing info */ |
656 | if (changes & BSS_CHANGED_EHT_PUNCTURING && vif->cfg.assoc && has_eht) | |
657 | link_changes |= LINK_CONTEXT_MODIFY_EHT_PARAMS; | |
658 | ||
659 | if (link_changes) { | |
22c58834 GG |
660 | ret = iwl_mvm_link_changed(mvm, vif, link_conf, link_changes, |
661 | true); | |
660eba5a MK |
662 | if (ret) |
663 | IWL_ERR(mvm, "failed to update link\n"); | |
664 | } | |
665 | ||
666 | ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); | |
667 | if (ret) | |
668 | IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); | |
669 | ||
22c58834 GG |
670 | memcpy(mvmvif->link[link_conf->link_id]->bssid, link_conf->bssid, |
671 | ETH_ALEN); | |
672 | ||
673 | iwl_mvm_bss_info_changed_station_common(mvm, vif, link_conf, changes); | |
674 | } | |
675 | ||
676 | static bool iwl_mvm_mld_vif_have_valid_ap_sta(struct iwl_mvm_vif *mvmvif) | |
677 | { | |
678 | int i; | |
679 | ||
680 | for_each_mvm_vif_valid_link(mvmvif, i) { | |
681 | if (mvmvif->link[i]->ap_sta_id != IWL_MVM_INVALID_STA) | |
682 | return true; | |
683 | } | |
684 | ||
685 | return false; | |
686 | } | |
687 | ||
688 | static void iwl_mvm_mld_vif_delete_all_stas(struct iwl_mvm *mvm, | |
689 | struct ieee80211_vif *vif) | |
690 | { | |
691 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
692 | int i, ret; | |
693 | ||
694 | if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) | |
695 | return; | |
696 | ||
22c58834 GG |
697 | for_each_mvm_vif_valid_link(mvmvif, i) { |
698 | struct iwl_mvm_vif_link_info *link = mvmvif->link[i]; | |
699 | ||
700 | if (!link) | |
701 | continue; | |
702 | ||
072573f6 | 703 | iwl_mvm_sec_key_remove_ap(mvm, vif, link, i); |
881d0548 | 704 | ret = iwl_mvm_mld_rm_sta_id(mvm, link->ap_sta_id); |
22c58834 GG |
705 | if (ret) |
706 | IWL_ERR(mvm, "failed to remove AP station\n"); | |
707 | ||
708 | link->ap_sta_id = IWL_MVM_INVALID_STA; | |
709 | } | |
710 | } | |
711 | ||
712 | static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, | |
713 | struct ieee80211_vif *vif, | |
714 | u64 changes) | |
715 | { | |
716 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
717 | struct ieee80211_bss_conf *link_conf; | |
718 | bool protect = false; | |
719 | unsigned int i; | |
720 | int ret; | |
721 | ||
8930ed56 JB |
722 | /* This might get called without active links during the |
723 | * chanctx switch, but we don't care about it anyway. | |
724 | */ | |
725 | if (changes == BSS_CHANGED_IDLE) | |
726 | return; | |
727 | ||
22c58834 GG |
728 | ret = iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); |
729 | if (ret) | |
730 | IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); | |
731 | ||
660eba5a MK |
732 | mvmvif->associated = vif->cfg.assoc; |
733 | ||
2d4caa1d GG |
734 | if (changes & BSS_CHANGED_ASSOC) { |
735 | if (vif->cfg.assoc) { | |
736 | /* clear statistics to get clean beacon counter */ | |
737 | iwl_mvm_request_statistics(mvm, true); | |
738 | iwl_mvm_sf_update(mvm, vif, false); | |
739 | iwl_mvm_power_vif_assoc(mvm, vif); | |
740 | ||
741 | for_each_mvm_vif_valid_link(mvmvif, i) { | |
742 | memset(&mvmvif->link[i]->beacon_stats, 0, | |
743 | sizeof(mvmvif->link[i]->beacon_stats)); | |
744 | ||
745 | if (vif->p2p) { | |
746 | iwl_mvm_update_smps(mvm, vif, | |
747 | IWL_MVM_SMPS_REQ_PROT, | |
748 | IEEE80211_SMPS_DYNAMIC, i); | |
749 | } | |
750 | ||
751 | rcu_read_lock(); | |
752 | link_conf = rcu_dereference(vif->link_conf[i]); | |
753 | if (link_conf && !link_conf->dtim_period) | |
754 | protect = true; | |
755 | rcu_read_unlock(); | |
756 | } | |
660eba5a | 757 | |
2d4caa1d GG |
758 | if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && |
759 | protect) { | |
760 | /* If we're not restarting and still haven't | |
761 | * heard a beacon (dtim period unknown) then | |
762 | * make sure we still have enough minimum time | |
763 | * remaining in the time event, since the auth | |
764 | * might actually have taken quite a while | |
765 | * (especially for SAE) and so the remaining | |
766 | * time could be small without us having heard | |
767 | * a beacon yet. | |
768 | */ | |
769 | iwl_mvm_protect_assoc(mvm, vif, 0); | |
660eba5a | 770 | } |
22c58834 | 771 | |
2d4caa1d GG |
772 | iwl_mvm_sf_update(mvm, vif, false); |
773 | ||
774 | /* FIXME: need to decide about misbehaving AP handling */ | |
775 | iwl_mvm_power_vif_assoc(mvm, vif); | |
776 | } else if (iwl_mvm_mld_vif_have_valid_ap_sta(mvmvif)) { | |
777 | iwl_mvm_mei_host_disassociated(mvm); | |
22c58834 | 778 | |
2d4caa1d GG |
779 | /* If update fails - SF might be running in associated |
780 | * mode while disassociated - which is forbidden. | |
660eba5a | 781 | */ |
2d4caa1d GG |
782 | ret = iwl_mvm_sf_update(mvm, vif, false); |
783 | WARN_ONCE(ret && | |
784 | !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, | |
785 | &mvm->status), | |
786 | "Failed to update SF upon disassociation\n"); | |
787 | ||
788 | /* If we get an assert during the connection (after the | |
789 | * station has been added, but before the vif is set | |
790 | * to associated), mac80211 will re-add the station and | |
791 | * then configure the vif. Since the vif is not | |
792 | * associated, we would remove the station here and | |
793 | * this would fail the recovery. | |
794 | */ | |
795 | iwl_mvm_mld_vif_delete_all_stas(mvm, vif); | |
660eba5a MK |
796 | } |
797 | ||
2d4caa1d | 798 | iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes); |
660eba5a MK |
799 | } |
800 | ||
2d4caa1d GG |
801 | if (changes & BSS_CHANGED_PS) { |
802 | ret = iwl_mvm_power_update_mac(mvm); | |
803 | if (ret) | |
804 | IWL_ERR(mvm, "failed to update power mode\n"); | |
805 | } | |
660eba5a MK |
806 | } |
807 | ||
808 | static void | |
22c58834 GG |
809 | iwl_mvm_mld_link_info_changed_ap_ibss(struct iwl_mvm *mvm, |
810 | struct ieee80211_vif *vif, | |
811 | struct ieee80211_bss_conf *link_conf, | |
812 | u64 changes) | |
660eba5a MK |
813 | { |
814 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
815 | u32 link_changes = LINK_CONTEXT_MODIFY_PROTECT_FLAGS | | |
816 | LINK_CONTEXT_MODIFY_QOS_PARAMS; | |
817 | ||
818 | /* Changes will be applied when the AP/IBSS is started */ | |
819 | if (!mvmvif->ap_ibss_active) | |
820 | return; | |
821 | ||
22c58834 GG |
822 | if (link_conf->he_support) |
823 | link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; | |
824 | ||
a3ff9303 IP |
825 | if (changes & BSS_CHANGED_ERP_SLOT) |
826 | link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO; | |
827 | ||
828 | if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_SLOT | | |
829 | BSS_CHANGED_HT | | |
22c58834 GG |
830 | BSS_CHANGED_BANDWIDTH | BSS_CHANGED_QOS | |
831 | BSS_CHANGED_HE_BSS_COLOR) && | |
832 | iwl_mvm_link_changed(mvm, vif, link_conf, | |
cacc1d42 | 833 | link_changes, true)) |
660eba5a MK |
834 | IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); |
835 | ||
836 | /* Need to send a new beacon template to the FW */ | |
837 | if (changes & BSS_CHANGED_BEACON && | |
22c58834 | 838 | iwl_mvm_mac_ctxt_beacon_changed(mvm, vif, link_conf)) |
660eba5a MK |
839 | IWL_WARN(mvm, "Failed updating beacon data\n"); |
840 | ||
22c58834 | 841 | /* FIXME: need to decide if we need FTM responder per link */ |
660eba5a | 842 | if (changes & BSS_CHANGED_FTM_RESPONDER) { |
fd940de7 | 843 | int ret = iwl_mvm_ftm_start_responder(mvm, vif, link_conf); |
660eba5a MK |
844 | |
845 | if (ret) | |
846 | IWL_WARN(mvm, "Failed to enable FTM responder (%d)\n", | |
847 | ret); | |
848 | } | |
849 | } | |
850 | ||
22c58834 GG |
851 | static void iwl_mvm_mld_link_info_changed(struct ieee80211_hw *hw, |
852 | struct ieee80211_vif *vif, | |
853 | struct ieee80211_bss_conf *link_conf, | |
854 | u64 changes) | |
855 | { | |
856 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
857 | ||
858 | mutex_lock(&mvm->mutex); | |
859 | ||
860 | switch (vif->type) { | |
861 | case NL80211_IFTYPE_STATION: | |
862 | iwl_mvm_mld_link_info_changed_station(mvm, vif, link_conf, | |
863 | changes); | |
864 | break; | |
865 | case NL80211_IFTYPE_AP: | |
866 | case NL80211_IFTYPE_ADHOC: | |
867 | iwl_mvm_mld_link_info_changed_ap_ibss(mvm, vif, link_conf, | |
868 | changes); | |
869 | break; | |
870 | case NL80211_IFTYPE_MONITOR: | |
871 | if (changes & BSS_CHANGED_MU_GROUPS) | |
872 | iwl_mvm_update_mu_groups(mvm, vif); | |
873 | break; | |
874 | default: | |
875 | /* shouldn't happen */ | |
876 | WARN_ON_ONCE(1); | |
877 | } | |
878 | ||
879 | if (changes & BSS_CHANGED_TXPOWER) { | |
880 | IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d dBm\n", | |
881 | link_conf->txpower); | |
882 | iwl_mvm_set_tx_power(mvm, vif, link_conf->txpower); | |
883 | } | |
884 | ||
885 | mutex_unlock(&mvm->mutex); | |
886 | } | |
887 | ||
888 | static void iwl_mvm_mld_vif_cfg_changed(struct ieee80211_hw *hw, | |
889 | struct ieee80211_vif *vif, | |
890 | u64 changes) | |
660eba5a | 891 | { |
22c58834 | 892 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); |
660eba5a | 893 | |
22c58834 GG |
894 | mutex_lock(&mvm->mutex); |
895 | ||
896 | if (changes & BSS_CHANGED_IDLE && !vif->cfg.idle) | |
897 | iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true); | |
898 | ||
899 | if (vif->type == NL80211_IFTYPE_STATION) | |
900 | iwl_mvm_mld_vif_cfg_changed_station(mvm, vif, changes); | |
901 | ||
902 | mutex_unlock(&mvm->mutex); | |
660eba5a MK |
903 | } |
904 | ||
905 | static int | |
906 | iwl_mvm_mld_switch_vif_chanctx(struct ieee80211_hw *hw, | |
907 | struct ieee80211_vif_chanctx_switch *vifs, | |
908 | int n_vifs, | |
909 | enum ieee80211_chanctx_switch_mode mode) | |
910 | { | |
a2906ea6 | 911 | static const struct iwl_mvm_switch_vif_chanctx_ops ops = { |
660eba5a MK |
912 | .__assign_vif_chanctx = __iwl_mvm_mld_assign_vif_chanctx, |
913 | .__unassign_vif_chanctx = __iwl_mvm_mld_unassign_vif_chanctx, | |
914 | }; | |
915 | ||
916 | return iwl_mvm_switch_vif_chanctx_common(hw, vifs, n_vifs, mode, &ops); | |
917 | } | |
918 | ||
919 | static void iwl_mvm_mld_config_iface_filter(struct ieee80211_hw *hw, | |
920 | struct ieee80211_vif *vif, | |
921 | unsigned int filter_flags, | |
922 | unsigned int changed_flags) | |
923 | { | |
924 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
925 | ||
926 | /* We support only filter for probe requests */ | |
927 | if (!(changed_flags & FIF_PROBE_REQ)) | |
928 | return; | |
929 | ||
930 | /* Supported only for p2p client interfaces */ | |
931 | if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc || | |
932 | !vif->p2p) | |
933 | return; | |
934 | ||
935 | mutex_lock(&mvm->mutex); | |
936 | iwl_mvm_mld_mac_ctxt_changed(mvm, vif, false); | |
937 | mutex_unlock(&mvm->mutex); | |
938 | } | |
939 | ||
940 | static int | |
941 | iwl_mvm_mld_mac_conf_tx(struct ieee80211_hw *hw, | |
942 | struct ieee80211_vif *vif, | |
943 | unsigned int link_id, u16 ac, | |
944 | const struct ieee80211_tx_queue_params *params) | |
945 | { | |
946 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
947 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
33acbe6a | 948 | struct iwl_mvm_vif_link_info *mvm_link = mvmvif->link[link_id]; |
660eba5a | 949 | |
33acbe6a JB |
950 | if (!mvm_link) |
951 | return -EINVAL; | |
952 | ||
953 | mvm_link->queue_params[ac] = *params; | |
660eba5a MK |
954 | |
955 | /* No need to update right away, we'll get BSS_CHANGED_QOS | |
956 | * The exception is P2P_DEVICE interface which needs immediate update. | |
957 | */ | |
958 | if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { | |
959 | int ret; | |
960 | ||
961 | mutex_lock(&mvm->mutex); | |
cacc1d42 | 962 | ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, |
660eba5a MK |
963 | LINK_CONTEXT_MODIFY_QOS_PARAMS, |
964 | true); | |
965 | mutex_unlock(&mvm->mutex); | |
966 | return ret; | |
967 | } | |
968 | return 0; | |
969 | } | |
970 | ||
fe8b2ad3 MK |
971 | static int iwl_mvm_link_switch_phy_ctx(struct iwl_mvm *mvm, |
972 | struct ieee80211_vif *vif, | |
973 | struct iwl_mvm_phy_ctxt *new_phy_ctxt) | |
974 | { | |
975 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | |
976 | int ret = 0; | |
977 | ||
978 | lockdep_assert_held(&mvm->mutex); | |
979 | ||
980 | /* Inorder to change the phy_ctx of a link, the link needs to be | |
981 | * inactive. Therefore, first deactivate the link, then change its | |
982 | * phy_ctx, and then activate it again. | |
983 | */ | |
cacc1d42 GG |
984 | ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, |
985 | LINK_CONTEXT_MODIFY_ACTIVE, false); | |
fe8b2ad3 MK |
986 | if (WARN(ret, "Failed to deactivate link\n")) |
987 | return ret; | |
988 | ||
989 | iwl_mvm_phy_ctxt_unref(mvm, mvmvif->deflink.phy_ctxt); | |
990 | ||
991 | mvmvif->deflink.phy_ctxt = new_phy_ctxt; | |
992 | ||
cacc1d42 | 993 | ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, 0, false); |
fe8b2ad3 MK |
994 | if (WARN(ret, "Failed to deactivate link\n")) |
995 | return ret; | |
996 | ||
cacc1d42 GG |
997 | ret = iwl_mvm_link_changed(mvm, vif, &vif->bss_conf, |
998 | LINK_CONTEXT_MODIFY_ACTIVE, true); | |
fe8b2ad3 MK |
999 | WARN(ret, "Failed binding P2P_DEVICE\n"); |
1000 | return ret; | |
1001 | } | |
1002 | ||
1003 | static int iwl_mvm_mld_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
1004 | struct ieee80211_channel *channel, int duration, | |
1005 | enum ieee80211_roc_type type) | |
1006 | { | |
a2906ea6 | 1007 | static const struct iwl_mvm_roc_ops ops = { |
fe8b2ad3 MK |
1008 | .add_aux_sta_for_hs20 = iwl_mvm_mld_add_aux_sta, |
1009 | .switch_phy_ctxt = iwl_mvm_link_switch_phy_ctx, | |
1010 | }; | |
1011 | ||
1012 | return iwl_mvm_roc_common(hw, vif, channel, duration, type, &ops); | |
1013 | } | |
bf976c81 JB |
1014 | |
1015 | static int | |
1016 | iwl_mvm_mld_change_vif_links(struct ieee80211_hw *hw, | |
1017 | struct ieee80211_vif *vif, | |
1018 | u16 old_links, u16 new_links, | |
1019 | struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) | |
1020 | { | |
1021 | struct iwl_mvm_vif_link_info *new_link[IEEE80211_MLD_MAX_NUM_LINKS] = {}; | |
12bacfc2 | 1022 | unsigned int n_active = iwl_mvm_mld_count_active_links(vif); |
bf976c81 JB |
1023 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
1024 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | |
1025 | u16 removed = old_links & ~new_links; | |
1026 | u16 added = new_links & ~old_links; | |
1027 | int err, i; | |
1028 | ||
12bacfc2 MK |
1029 | if (hweight16(new_links) > 1 && |
1030 | n_active > iwl_mvm_max_active_links(mvm, vif)) | |
1031 | return -EOPNOTSUPP; | |
bf976c81 JB |
1032 | |
1033 | for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { | |
1034 | int r; | |
1035 | ||
1036 | if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) | |
1037 | break; | |
1038 | ||
1039 | if (!(added & BIT(i))) | |
1040 | continue; | |
1041 | new_link[i] = kzalloc(sizeof(*new_link[i]), GFP_KERNEL); | |
1042 | if (!new_link[i]) { | |
1043 | err = -ENOMEM; | |
1044 | goto free; | |
1045 | } | |
1046 | ||
1047 | new_link[i]->bcast_sta.sta_id = IWL_MVM_INVALID_STA; | |
1048 | new_link[i]->mcast_sta.sta_id = IWL_MVM_INVALID_STA; | |
1049 | new_link[i]->ap_sta_id = IWL_MVM_INVALID_STA; | |
1050 | new_link[i]->fw_link_id = IWL_MVM_FW_LINK_ID_INVALID; | |
1051 | ||
1052 | for (r = 0; r < NUM_IWL_MVM_SMPS_REQ; r++) | |
1053 | new_link[i]->smps_requests[r] = | |
1054 | IEEE80211_SMPS_AUTOMATIC; | |
1055 | } | |
1056 | ||
1057 | mutex_lock(&mvm->mutex); | |
1058 | ||
1059 | if (old_links == 0) { | |
1060 | err = iwl_mvm_disable_link(mvm, vif, &vif->bss_conf); | |
1061 | if (err) | |
1062 | goto out_err; | |
1063 | mvmvif->link[0] = NULL; | |
1064 | } | |
1065 | ||
1066 | for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { | |
1067 | if (removed & BIT(i)) { | |
1068 | struct ieee80211_bss_conf *link_conf = old[i]; | |
1069 | ||
1070 | err = iwl_mvm_disable_link(mvm, vif, link_conf); | |
1071 | if (err) | |
1072 | goto out_err; | |
1073 | kfree(mvmvif->link[i]); | |
1074 | mvmvif->link[i] = NULL; | |
12bacfc2 | 1075 | } else if (added & BIT(i)) { |
bf976c81 JB |
1076 | struct ieee80211_bss_conf *link_conf; |
1077 | ||
0d504ca1 | 1078 | link_conf = link_conf_dereference_protected(vif, i); |
bf976c81 JB |
1079 | if (WARN_ON(!link_conf)) |
1080 | continue; | |
1081 | ||
1082 | if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, | |
1083 | &mvm->status)) | |
1084 | mvmvif->link[i] = new_link[i]; | |
1085 | new_link[i] = NULL; | |
1086 | err = iwl_mvm_add_link(mvm, vif, link_conf); | |
1087 | if (err) | |
1088 | goto out_err; | |
1089 | } | |
1090 | } | |
1091 | ||
12bacfc2 MK |
1092 | if (err) |
1093 | goto out_err; | |
1094 | ||
bf976c81 JB |
1095 | err = 0; |
1096 | if (new_links == 0) { | |
1097 | mvmvif->link[0] = &mvmvif->deflink; | |
1098 | err = iwl_mvm_add_link(mvm, vif, &vif->bss_conf); | |
1099 | } | |
1100 | ||
1101 | out_err: | |
1102 | /* we really don't have a good way to roll back here ... */ | |
1103 | mutex_unlock(&mvm->mutex); | |
1104 | ||
1105 | free: | |
1106 | for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) | |
1107 | kfree(new_link[i]); | |
1108 | return err; | |
1109 | } | |
1110 | ||
1111 | static int | |
1112 | iwl_mvm_mld_change_sta_links(struct ieee80211_hw *hw, | |
1113 | struct ieee80211_vif *vif, | |
1114 | struct ieee80211_sta *sta, | |
1115 | u16 old_links, u16 new_links) | |
1116 | { | |
6e4198d3 ST |
1117 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); |
1118 | int ret; | |
1119 | ||
1120 | mutex_lock(&mvm->mutex); | |
1121 | ret = iwl_mvm_mld_update_sta_links(mvm, vif, sta, old_links, new_links); | |
1122 | mutex_unlock(&mvm->mutex); | |
1123 | ||
1124 | return ret; | |
bf976c81 JB |
1125 | } |
1126 | ||
1ab26632 | 1127 | const struct ieee80211_ops iwl_mvm_mld_hw_ops = { |
cbce62a3 MK |
1128 | .tx = iwl_mvm_mac_tx, |
1129 | .wake_tx_queue = iwl_mvm_mac_wake_tx_queue, | |
1130 | .ampdu_action = iwl_mvm_mac_ampdu_action, | |
1131 | .get_antenna = iwl_mvm_op_get_antenna, | |
1132 | .start = iwl_mvm_mac_start, | |
1133 | .reconfig_complete = iwl_mvm_mac_reconfig_complete, | |
1134 | .stop = iwl_mvm_mac_stop, | |
1ab26632 | 1135 | .add_interface = iwl_mvm_mld_mac_add_interface, |
60efeca1 | 1136 | .remove_interface = iwl_mvm_mld_mac_remove_interface, |
cbce62a3 MK |
1137 | .config = iwl_mvm_mac_config, |
1138 | .prepare_multicast = iwl_mvm_prepare_multicast, | |
1139 | .configure_filter = iwl_mvm_configure_filter, | |
660eba5a | 1140 | .config_iface_filter = iwl_mvm_mld_config_iface_filter, |
22c58834 GG |
1141 | .link_info_changed = iwl_mvm_mld_link_info_changed, |
1142 | .vif_cfg_changed = iwl_mvm_mld_vif_cfg_changed, | |
cbce62a3 MK |
1143 | .hw_scan = iwl_mvm_mac_hw_scan, |
1144 | .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan, | |
1145 | .sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove, | |
1146 | .sta_state = iwl_mvm_mld_mac_sta_state, | |
1147 | .sta_notify = iwl_mvm_mac_sta_notify, | |
1148 | .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, | |
1149 | .release_buffered_frames = iwl_mvm_mac_release_buffered_frames, | |
1150 | .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, | |
1151 | .sta_rc_update = iwl_mvm_sta_rc_update, | |
1152 | .conf_tx = iwl_mvm_mld_mac_conf_tx, | |
1153 | .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, | |
1154 | .mgd_complete_tx = iwl_mvm_mac_mgd_complete_tx, | |
1155 | .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover, | |
1156 | .flush = iwl_mvm_mac_flush, | |
a6cc6ccb | 1157 | .flush_sta = iwl_mvm_mac_flush_sta, |
cbce62a3 MK |
1158 | .sched_scan_start = iwl_mvm_mac_sched_scan_start, |
1159 | .sched_scan_stop = iwl_mvm_mac_sched_scan_stop, | |
1160 | .set_key = iwl_mvm_mac_set_key, | |
1161 | .update_tkip_key = iwl_mvm_mac_update_tkip_key, | |
fe8b2ad3 MK |
1162 | .remain_on_channel = iwl_mvm_mld_roc, |
1163 | .cancel_remain_on_channel = iwl_mvm_cancel_roc, | |
cbce62a3 MK |
1164 | .add_chanctx = iwl_mvm_add_chanctx, |
1165 | .remove_chanctx = iwl_mvm_remove_chanctx, | |
1166 | .change_chanctx = iwl_mvm_change_chanctx, | |
50e81437 | 1167 | .assign_vif_chanctx = iwl_mvm_mld_assign_vif_chanctx, |
6f71e90e | 1168 | .unassign_vif_chanctx = iwl_mvm_mld_unassign_vif_chanctx, |
660eba5a | 1169 | .switch_vif_chanctx = iwl_mvm_mld_switch_vif_chanctx, |
cbce62a3 MK |
1170 | |
1171 | .start_ap = iwl_mvm_mld_start_ap, | |
1172 | .stop_ap = iwl_mvm_mld_stop_ap, | |
1173 | .join_ibss = iwl_mvm_mld_start_ibss, | |
1174 | .leave_ibss = iwl_mvm_mld_stop_ibss, | |
1175 | ||
1176 | .tx_last_beacon = iwl_mvm_tx_last_beacon, | |
1177 | ||
1178 | .set_tim = iwl_mvm_set_tim, | |
1179 | ||
1180 | .channel_switch = iwl_mvm_channel_switch, | |
1181 | .pre_channel_switch = iwl_mvm_pre_channel_switch, | |
03117f30 | 1182 | .post_channel_switch = iwl_mvm_post_channel_switch, |
cbce62a3 MK |
1183 | .abort_channel_switch = iwl_mvm_abort_channel_switch, |
1184 | .channel_switch_rx_beacon = iwl_mvm_channel_switch_rx_beacon, | |
1185 | ||
1186 | .tdls_channel_switch = iwl_mvm_tdls_channel_switch, | |
1187 | .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch, | |
1188 | .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch, | |
1189 | ||
1190 | .event_callback = iwl_mvm_mac_event_callback, | |
1191 | ||
1192 | .sync_rx_queues = iwl_mvm_sync_rx_queues, | |
1193 | ||
1194 | CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) | |
1195 | ||
1196 | #ifdef CONFIG_PM_SLEEP | |
1197 | /* look at d3.c */ | |
1198 | .suspend = iwl_mvm_suspend, | |
1199 | .resume = iwl_mvm_resume, | |
1200 | .set_wakeup = iwl_mvm_set_wakeup, | |
1201 | .set_rekey_data = iwl_mvm_set_rekey_data, | |
1202 | #if IS_ENABLED(CONFIG_IPV6) | |
1203 | .ipv6_addr_change = iwl_mvm_ipv6_addr_change, | |
1204 | #endif | |
1205 | .set_default_unicast_key = iwl_mvm_set_default_unicast_key, | |
1206 | #endif | |
1207 | .get_survey = iwl_mvm_mac_get_survey, | |
1208 | .sta_statistics = iwl_mvm_mac_sta_statistics, | |
1209 | .get_ftm_responder_stats = iwl_mvm_mac_get_ftm_responder_stats, | |
1210 | .start_pmsr = iwl_mvm_start_pmsr, | |
1211 | .abort_pmsr = iwl_mvm_abort_pmsr, | |
1212 | ||
1213 | #ifdef CONFIG_IWLWIFI_DEBUGFS | |
3f244876 | 1214 | .link_sta_add_debugfs = iwl_mvm_link_sta_add_debugfs, |
cbce62a3 | 1215 | #endif |
be8897e2 | 1216 | .set_hw_timestamp = iwl_mvm_set_hw_timestamp, |
bf976c81 JB |
1217 | |
1218 | .change_vif_links = iwl_mvm_mld_change_vif_links, | |
1219 | .change_sta_links = iwl_mvm_mld_change_sta_links, | |
1ab26632 | 1220 | }; |