1 /*
2 * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 /*
18 * DOC: contains MLO manager Link Switch related functionality
19 */
20 #include <wlan_mlo_mgr_link_switch.h>
21 #include <wlan_mlo_mgr_main.h>
22 #include <wlan_mlo_mgr_sta.h>
23 #include <wlan_serialization_api.h>
24 #include <wlan_cm_api.h>
25 #include <wlan_crypto_def_i.h>
26 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
27 #include "wlan_cm_roam_api.h"
28 #include <wlan_mlo_mgr_roam.h>
29 #endif
30 #include "host_diag_core_event.h"
31
mlo_mgr_update_link_info_mac_addr(struct wlan_objmgr_vdev * vdev,struct wlan_mlo_link_mac_update * ml_mac_update)32 void mlo_mgr_update_link_info_mac_addr(struct wlan_objmgr_vdev *vdev,
33 struct wlan_mlo_link_mac_update *ml_mac_update)
34 {
35 struct mlo_link_info *link_info;
36 uint8_t link_info_iter;
37 struct mlo_vdev_link_mac_info *link_mac_info;
38
39 if (!vdev || !vdev->mlo_dev_ctx || !ml_mac_update)
40 return;
41
42 link_mac_info = &ml_mac_update->link_mac_info[0];
43 link_info = &vdev->mlo_dev_ctx->link_ctx->links_info[0];
44
45 for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
46 link_info_iter++) {
47 qdf_mem_copy(&link_info->link_addr,
48 &link_mac_info->link_mac_addr,
49 QDF_MAC_ADDR_SIZE);
50
51 link_info->vdev_id = link_mac_info->vdev_id;
52 mlo_debug("Update STA Link info for vdev_id %d, link_addr:" QDF_MAC_ADDR_FMT,
53 link_info->vdev_id,
54 QDF_MAC_ADDR_REF(link_info->link_addr.bytes));
55 link_mac_info++;
56 link_info++;
57 }
58 }
59
mlo_mgr_update_ap_link_info(struct wlan_objmgr_vdev * vdev,uint8_t link_id,uint8_t * ap_link_addr,struct wlan_channel channel)60 void mlo_mgr_update_ap_link_info(struct wlan_objmgr_vdev *vdev, uint8_t link_id,
61 uint8_t *ap_link_addr,
62 struct wlan_channel channel)
63 {
64 struct mlo_link_info *link_info;
65 uint8_t link_info_iter;
66
67 if (!vdev || !vdev->mlo_dev_ctx || !ap_link_addr)
68 return;
69
70 link_info = &vdev->mlo_dev_ctx->link_ctx->links_info[0];
71 for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
72 link_info_iter++) {
73 if (qdf_is_macaddr_zero(&link_info->ap_link_addr))
74 break;
75
76 link_info++;
77 }
78
79 if (link_info_iter == WLAN_MAX_ML_BSS_LINKS)
80 return;
81
82 qdf_mem_copy(&link_info->ap_link_addr, ap_link_addr, QDF_MAC_ADDR_SIZE);
83
84 qdf_mem_copy(link_info->link_chan_info, &channel, sizeof(channel));
85 link_info->link_status_flags = 0;
86 link_info->link_id = link_id;
87 link_info->is_link_active = false;
88
89 mlo_debug("Update AP Link info for link_id: %d, vdev_id:%d, link_addr:" QDF_MAC_ADDR_FMT,
90 link_info->link_id, link_info->vdev_id,
91 QDF_MAC_ADDR_REF(link_info->ap_link_addr.bytes));
92 }
93
mlo_mgr_clear_ap_link_info(struct wlan_objmgr_vdev * vdev,uint8_t * ap_link_addr)94 void mlo_mgr_clear_ap_link_info(struct wlan_objmgr_vdev *vdev,
95 uint8_t *ap_link_addr)
96 {
97 struct mlo_link_info *link_info;
98 uint8_t link_info_iter;
99
100 if (!vdev || !vdev->mlo_dev_ctx || !ap_link_addr)
101 return;
102
103 link_info = &vdev->mlo_dev_ctx->link_ctx->links_info[0];
104 for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
105 link_info_iter++) {
106 if (qdf_is_macaddr_equal(&link_info->ap_link_addr,
107 (struct qdf_mac_addr *)ap_link_addr)) {
108 mlo_debug("Clear AP link info for link_id: %d, vdev_id:%d, link_addr:" QDF_MAC_ADDR_FMT,
109 link_info->link_id, link_info->vdev_id,
110 QDF_MAC_ADDR_REF(link_info->ap_link_addr.bytes));
111
112 qdf_mem_zero(&link_info->ap_link_addr,
113 QDF_MAC_ADDR_SIZE);
114 qdf_mem_zero(link_info->link_chan_info,
115 sizeof(*link_info->link_chan_info));
116 link_info->link_id = WLAN_INVALID_LINK_ID;
117 link_info->link_status_flags = 0;
118
119 return;
120 }
121
122 link_info++;
123 }
124 }
125
mlo_mgr_update_ap_channel_info(struct wlan_objmgr_vdev * vdev,uint8_t link_id,uint8_t * ap_link_addr,struct wlan_channel channel)126 void mlo_mgr_update_ap_channel_info(struct wlan_objmgr_vdev *vdev, uint8_t link_id,
127 uint8_t *ap_link_addr,
128 struct wlan_channel channel)
129 {
130 struct mlo_link_info *link_info;
131
132 if (!vdev || !vdev->mlo_dev_ctx || !ap_link_addr)
133 return;
134
135 link_info = mlo_mgr_get_ap_link_by_link_id(vdev->mlo_dev_ctx, link_id);
136 if (!link_info)
137 return;
138
139 qdf_mem_copy(link_info->link_chan_info, &channel,
140 sizeof(*link_info->link_chan_info));
141
142 mlo_debug("update AP channel info link_id: %d, vdev_id:%d, link_addr:" QDF_MAC_ADDR_FMT,
143 link_info->link_id, link_info->vdev_id,
144 QDF_MAC_ADDR_REF(link_info->ap_link_addr.bytes));
145 mlo_debug("ch_freq: %d, freq1: %d, freq2: %d, phy_mode: %d",
146 link_info->link_chan_info->ch_freq,
147 link_info->link_chan_info->ch_cfreq1,
148 link_info->link_chan_info->ch_cfreq2,
149 link_info->link_chan_info->ch_phymode);
150 }
151
mlo_mgr_update_link_info_reset(struct wlan_objmgr_psoc * psoc,struct wlan_mlo_dev_context * ml_dev)152 void mlo_mgr_update_link_info_reset(struct wlan_objmgr_psoc *psoc,
153 struct wlan_mlo_dev_context *ml_dev)
154 {
155 struct mlo_link_info *link_info;
156 uint8_t link_info_iter;
157
158 if (!ml_dev)
159 return;
160
161 link_info = &ml_dev->link_ctx->links_info[0];
162
163 for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
164 link_info_iter++) {
165 if (!qdf_is_macaddr_zero(&link_info->ap_link_addr) &&
166 !qdf_is_macaddr_zero(&link_info->link_addr))
167 wlan_crypto_free_key_by_link_id(
168 psoc,
169 &link_info->link_addr,
170 link_info->link_id);
171 qdf_mem_zero(&link_info->link_addr, QDF_MAC_ADDR_SIZE);
172 qdf_mem_zero(&link_info->ap_link_addr, QDF_MAC_ADDR_SIZE);
173 qdf_mem_zero(link_info->link_chan_info,
174 sizeof(*link_info->link_chan_info));
175 link_info->vdev_id = WLAN_INVALID_VDEV_ID;
176 link_info->link_id = WLAN_INVALID_LINK_ID;
177 link_info->link_status_flags = 0;
178 link_info++;
179 }
180 }
181
mlo_mgr_reset_ap_link_info(struct wlan_objmgr_vdev * vdev)182 void mlo_mgr_reset_ap_link_info(struct wlan_objmgr_vdev *vdev)
183 {
184 struct mlo_link_info *link_info;
185 uint8_t link_info_iter;
186 struct wlan_objmgr_psoc *psoc;
187
188 if (!vdev || !vdev->mlo_dev_ctx || !vdev->mlo_dev_ctx->link_ctx)
189 return;
190
191 psoc = wlan_vdev_get_psoc(vdev);
192 if (!psoc) {
193 mlo_err("psoc NULL");
194 return;
195 }
196
197 link_info = &vdev->mlo_dev_ctx->link_ctx->links_info[0];
198
199 for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
200 link_info_iter++) {
201 if (!qdf_is_macaddr_zero(&link_info->ap_link_addr) &&
202 !qdf_is_macaddr_zero(&link_info->link_addr))
203 wlan_crypto_free_key_by_link_id(
204 psoc,
205 &link_info->link_addr,
206 link_info->link_id);
207 qdf_mem_zero(&link_info->ap_link_addr, QDF_MAC_ADDR_SIZE);
208 qdf_mem_zero(link_info->link_chan_info,
209 sizeof(*link_info->link_chan_info));
210 link_info->link_id = WLAN_INVALID_LINK_ID;
211 link_info->link_status_flags = 0;
212 link_info++;
213 }
214 }
215
216 struct mlo_link_info
mlo_mgr_get_ap_link(struct wlan_objmgr_vdev * vdev)217 *mlo_mgr_get_ap_link(struct wlan_objmgr_vdev *vdev)
218 {
219 if (!vdev || !vdev->mlo_dev_ctx)
220 return NULL;
221
222 return &vdev->mlo_dev_ctx->link_ctx->links_info[0];
223 }
224
225 static
mlo_mgr_alloc_link_info_wmi_chan(struct wlan_mlo_dev_context * ml_dev)226 void mlo_mgr_alloc_link_info_wmi_chan(struct wlan_mlo_dev_context *ml_dev)
227 {
228 struct mlo_link_info *link_info;
229 uint8_t link_info_iter;
230
231 if (!ml_dev)
232 return;
233
234 link_info = &ml_dev->link_ctx->links_info[0];
235 for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
236 link_info_iter++) {
237 link_info->link_chan_info =
238 qdf_mem_malloc(sizeof(*link_info->link_chan_info));
239 if (!link_info->link_chan_info)
240 return;
241 link_info++;
242 }
243 }
244
245 static
mlo_mgr_free_link_info_wmi_chan(struct wlan_mlo_dev_context * ml_dev)246 void mlo_mgr_free_link_info_wmi_chan(struct wlan_mlo_dev_context *ml_dev)
247 {
248 struct mlo_link_info *link_info;
249 uint8_t link_info_iter;
250
251 if (!ml_dev)
252 return;
253
254 link_info = &ml_dev->link_ctx->links_info[0];
255 for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
256 link_info_iter++) {
257 if (link_info->link_chan_info)
258 qdf_mem_free(link_info->link_chan_info);
259 link_info++;
260 }
261 }
262
263 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
264 struct mlo_link_info
mlo_mgr_get_ap_link_by_link_id(struct wlan_mlo_dev_context * mlo_dev_ctx,int link_id)265 *mlo_mgr_get_ap_link_by_link_id(struct wlan_mlo_dev_context *mlo_dev_ctx,
266 int link_id)
267 {
268 struct mlo_link_info *link_info;
269 uint8_t link_info_iter;
270
271 if (!mlo_dev_ctx || link_id < 0 || link_id > 15)
272 return NULL;
273
274 link_info = &mlo_dev_ctx->link_ctx->links_info[0];
275 for (link_info_iter = 0; link_info_iter < WLAN_MAX_ML_BSS_LINKS;
276 link_info_iter++) {
277 if (link_info->link_id == link_id)
278 return link_info;
279 link_info++;
280 }
281
282 return NULL;
283 }
284
mlo_mgr_update_csa_link_info(struct wlan_objmgr_pdev * pdev,struct wlan_mlo_dev_context * mlo_dev_ctx,struct csa_offload_params * csa_param,uint8_t link_id)285 bool mlo_mgr_update_csa_link_info(struct wlan_objmgr_pdev *pdev,
286 struct wlan_mlo_dev_context *mlo_dev_ctx,
287 struct csa_offload_params *csa_param,
288 uint8_t link_id)
289 {
290 struct mlo_link_info *link_info;
291 uint16_t bw_val;
292 uint32_t ch_cfreq1, ch_cfreq2;
293
294 if (!mlo_dev_ctx) {
295 mlo_err("invalid mlo dev ctx");
296 goto done;
297 }
298
299 bw_val = wlan_reg_get_bw_value(csa_param->new_ch_width);
300
301 link_info = mlo_mgr_get_ap_link_by_link_id(mlo_dev_ctx, link_id);
302 if (!link_info) {
303 mlo_err("invalid link_info");
304 goto done;
305 }
306
307 link_info->link_chan_info->ch_freq = csa_param->csa_chan_freq;
308
309 if (wlan_reg_is_6ghz_chan_freq(csa_param->csa_chan_freq)) {
310 ch_cfreq1 = wlan_reg_compute_6g_center_freq_from_cfi(
311 csa_param->new_ch_freq_seg1);
312 ch_cfreq2 = wlan_reg_compute_6g_center_freq_from_cfi(
313 csa_param->new_ch_freq_seg2);
314 } else {
315 ch_cfreq1 = wlan_reg_legacy_chan_to_freq(pdev,
316 csa_param->new_ch_freq_seg1);
317 ch_cfreq2 = wlan_reg_legacy_chan_to_freq(pdev,
318 csa_param->new_ch_freq_seg2);
319 }
320
321 link_info->link_chan_info->ch_cfreq1 = ch_cfreq1;
322 link_info->link_chan_info->ch_cfreq2 = ch_cfreq2;
323
324 link_info->link_chan_info->ch_phymode = wlan_eht_chan_phy_mode(
325 csa_param->csa_chan_freq,
326 bw_val, csa_param->new_ch_width);
327
328 mlo_debug("CSA: freq: %d, cfreq1: %d, cfreq2: %d, bw: %d, phymode:%d",
329 link_info->link_chan_info->ch_freq, ch_cfreq1, ch_cfreq2,
330 bw_val, link_info->link_chan_info->ch_phymode);
331
332 return true;
333 done:
334 return false;
335 }
336
337 struct wlan_objmgr_vdev *
mlo_mgr_link_switch_get_assoc_vdev(struct wlan_objmgr_vdev * vdev)338 mlo_mgr_link_switch_get_assoc_vdev(struct wlan_objmgr_vdev *vdev)
339 {
340 uint8_t vdev_id;
341 struct wlan_objmgr_psoc *psoc;
342 struct wlan_objmgr_vdev *assoc_vdev;
343
344 if (!vdev)
345 return NULL;
346
347 if (!mlo_mgr_is_link_switch_on_assoc_vdev(vdev))
348 return NULL;
349
350 vdev_id = vdev->mlo_dev_ctx->link_ctx->last_req.vdev_id;
351 psoc = wlan_vdev_get_psoc(vdev);
352 if (!psoc) {
353 mlo_err("PSOC NULL");
354 return NULL;
355 }
356
357 assoc_vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
358 WLAN_MLO_MGR_ID);
359
360 return assoc_vdev;
361 }
362
mlo_mgr_is_link_switch_in_progress(struct wlan_objmgr_vdev * vdev)363 bool mlo_mgr_is_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
364 {
365 enum mlo_link_switch_req_state state;
366 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx;
367
368 if (!mlo_dev_ctx)
369 return false;
370
371 state = mlo_mgr_link_switch_get_curr_state(mlo_dev_ctx);
372 return (state > MLO_LINK_SWITCH_STATE_INIT);
373 }
374
mlo_mgr_is_link_switch_on_assoc_vdev(struct wlan_objmgr_vdev * vdev)375 bool mlo_mgr_is_link_switch_on_assoc_vdev(struct wlan_objmgr_vdev *vdev)
376 {
377 if (!mlo_mgr_is_link_switch_in_progress(vdev))
378 return false;
379
380 return vdev->mlo_dev_ctx->link_ctx->last_req.restore_vdev_flag;
381 }
382
mlo_mgr_link_switch_init_state(struct wlan_mlo_dev_context * mlo_dev_ctx)383 void mlo_mgr_link_switch_init_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
384 {
385 mlo_dev_lock_acquire(mlo_dev_ctx);
386 mlo_dev_ctx->link_ctx->last_req.state = MLO_LINK_SWITCH_STATE_IDLE;
387 mlo_dev_lock_release(mlo_dev_ctx);
388 }
389
390 QDF_STATUS
mlo_mgr_link_switch_trans_next_state(struct wlan_mlo_dev_context * mlo_dev_ctx)391 mlo_mgr_link_switch_trans_next_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
392 {
393 QDF_STATUS status = QDF_STATUS_SUCCESS;
394 enum mlo_link_switch_req_state cur_state, next_state;
395
396 mlo_dev_lock_acquire(mlo_dev_ctx);
397 cur_state = mlo_dev_ctx->link_ctx->last_req.state;
398 switch (cur_state) {
399 case MLO_LINK_SWITCH_STATE_IDLE:
400 next_state = MLO_LINK_SWITCH_STATE_INIT;
401 break;
402 case MLO_LINK_SWITCH_STATE_INIT:
403 next_state = MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK;
404 break;
405 case MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK:
406 next_state = MLO_LINK_SWITCH_STATE_SET_MAC_ADDR;
407 break;
408 case MLO_LINK_SWITCH_STATE_SET_MAC_ADDR:
409 next_state = MLO_LINK_SWITCH_STATE_CONNECT_NEW_LINK;
410 break;
411 case MLO_LINK_SWITCH_STATE_CONNECT_NEW_LINK:
412 next_state = MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS;
413 break;
414 case MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS:
415 next_state = MLO_LINK_SWITCH_STATE_IDLE;
416 break;
417 case MLO_LINK_SWITCH_STATE_ABORT_TRANS:
418 next_state = cur_state;
419 status = QDF_STATUS_E_PERM;
420 mlo_debug("State transition not allowed");
421 break;
422 default:
423 QDF_ASSERT(0);
424 break;
425 }
426 mlo_dev_ctx->link_ctx->last_req.state = next_state;
427 mlo_dev_lock_release(mlo_dev_ctx);
428
429 return status;
430 }
431
432 void
mlo_mgr_link_switch_trans_abort_state(struct wlan_mlo_dev_context * mlo_dev_ctx)433 mlo_mgr_link_switch_trans_abort_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
434 {
435 enum mlo_link_switch_req_state next_state =
436 MLO_LINK_SWITCH_STATE_ABORT_TRANS;
437
438 mlo_dev_lock_acquire(mlo_dev_ctx);
439 mlo_dev_ctx->link_ctx->last_req.state = next_state;
440 mlo_dev_lock_release(mlo_dev_ctx);
441 }
442
443 enum mlo_link_switch_req_state
mlo_mgr_link_switch_get_curr_state(struct wlan_mlo_dev_context * mlo_dev_ctx)444 mlo_mgr_link_switch_get_curr_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
445 {
446 enum mlo_link_switch_req_state state;
447
448 mlo_dev_lock_acquire(mlo_dev_ctx);
449 state = mlo_dev_ctx->link_ctx->last_req.state;
450 mlo_dev_lock_release(mlo_dev_ctx);
451
452 return state;
453 }
454
455 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
456 static void
mlo_mgr_reset_roam_state_for_link_vdev(struct wlan_objmgr_vdev * vdev,struct wlan_objmgr_vdev * assoc_vdev)457 mlo_mgr_reset_roam_state_for_link_vdev(struct wlan_objmgr_vdev *vdev,
458 struct wlan_objmgr_vdev *assoc_vdev)
459 {
460 QDF_STATUS status;
461
462 status = wlan_cm_roam_state_change(wlan_vdev_get_pdev(vdev),
463 wlan_vdev_get_id(assoc_vdev),
464 WLAN_ROAM_DEINIT,
465 REASON_ROAM_LINK_SWITCH_ASSOC_VDEV_CHANGE);
466 if (QDF_IS_STATUS_ERROR(status))
467 mlo_err("vdev:%d failed to change RSO state to deinit",
468 wlan_vdev_get_id(assoc_vdev));
469 }
470
471 static void
mlo_mgr_restore_rso_upon_link_switch_failure(struct wlan_objmgr_vdev * vdev)472 mlo_mgr_restore_rso_upon_link_switch_failure(struct wlan_objmgr_vdev *vdev)
473 {
474 wlan_cm_roam_state_change(wlan_vdev_get_pdev(vdev),
475 wlan_vdev_get_id(vdev),
476 WLAN_ROAM_RSO_ENABLED,
477 REASON_CONNECT);
478 }
479 #else
480 static inline void
mlo_mgr_reset_roam_state_for_link_vdev(struct wlan_objmgr_vdev * vdev,struct wlan_objmgr_vdev * assoc_vdev)481 mlo_mgr_reset_roam_state_for_link_vdev(struct wlan_objmgr_vdev *vdev,
482 struct wlan_objmgr_vdev *assoc_vdev)
483 {}
484
485 static inline void
mlo_mgr_restore_rso_upon_link_switch_failure(struct wlan_objmgr_vdev * vdev)486 mlo_mgr_restore_rso_upon_link_switch_failure(struct wlan_objmgr_vdev *vdev)
487 {}
488 #endif
489
490 static QDF_STATUS
mlo_mgr_link_switch_osif_notification(struct wlan_objmgr_vdev * vdev,struct wlan_mlo_link_switch_req * lswitch_req)491 mlo_mgr_link_switch_osif_notification(struct wlan_objmgr_vdev *vdev,
492 struct wlan_mlo_link_switch_req *lswitch_req)
493 {
494 uint8_t idx;
495 uint16_t vdev_count;
496 struct wlan_objmgr_vdev *assoc_vdev;
497 struct wlan_mlo_sta *sta_ctx;
498 struct wlan_objmgr_vdev *vdev_list[WLAN_UMAC_MLO_MAX_VDEVS];
499 QDF_STATUS status = QDF_STATUS_E_INVAL;
500 struct mlo_mgr_context *g_mlo_ctx = wlan_objmgr_get_mlo_ctx();
501 QDF_STATUS(*cb)(struct wlan_objmgr_vdev *vdev,
502 uint8_t non_trans_vdev_id);
503
504 if (!vdev->mlo_dev_ctx)
505 return status;
506
507 sta_ctx = vdev->mlo_dev_ctx->sta_ctx;
508 if (!sta_ctx)
509 return status;
510
511 assoc_vdev = wlan_mlo_get_assoc_link_vdev(vdev);
512 if (!assoc_vdev)
513 return status;
514
515 cb = g_mlo_ctx->osif_ops->mlo_mgr_osif_link_switch_notification;
516
517 if (lswitch_req->restore_vdev_flag) {
518 wlan_vdev_mlme_clear_mlo_link_vdev(vdev);
519 wlan_vdev_mlme_set_mlo_link_vdev(assoc_vdev);
520 mlo_mgr_reset_roam_state_for_link_vdev(vdev, assoc_vdev);
521
522 lswitch_req->restore_vdev_flag = false;
523
524 status = cb(assoc_vdev, wlan_vdev_get_id(vdev));
525 if (QDF_IS_STATUS_ERROR(status))
526 mlo_debug("OSIF deflink restore failed");
527
528 return status;
529 }
530
531 if (wlan_vdev_get_id(assoc_vdev) != lswitch_req->vdev_id) {
532 mlo_debug("Not on assoc VDEV no need to swap");
533 return QDF_STATUS_SUCCESS;
534 }
535
536 mlo_sta_get_vdev_list(vdev, &vdev_count, vdev_list);
537 for (idx = 0; idx < vdev_count; idx++) {
538 if (wlan_vdev_get_id(vdev_list[idx]) != lswitch_req->vdev_id &&
539 qdf_test_bit(idx, sta_ctx->wlan_connected_links)) {
540 wlan_vdev_mlme_clear_mlo_link_vdev(vdev_list[idx]);
541 wlan_vdev_mlme_set_mlo_link_vdev(assoc_vdev);
542 lswitch_req->restore_vdev_flag = true;
543
544 status = cb(assoc_vdev,
545 wlan_vdev_get_id(vdev_list[idx]));
546 break;
547 }
548
549 mlo_release_vdev_ref(vdev_list[idx]);
550 }
551
552 for (; idx < vdev_count; idx++)
553 mlo_release_vdev_ref(vdev_list[idx]);
554
555 return status;
556 }
557
558 QDF_STATUS
mlo_mgr_link_switch_notification(struct wlan_objmgr_vdev * vdev,struct wlan_mlo_link_switch_req * lswitch_req,enum wlan_mlo_link_switch_notify_reason notify_reason)559 mlo_mgr_link_switch_notification(struct wlan_objmgr_vdev *vdev,
560 struct wlan_mlo_link_switch_req *lswitch_req,
561 enum wlan_mlo_link_switch_notify_reason notify_reason)
562 {
563 QDF_STATUS status;
564
565 switch (notify_reason) {
566 case MLO_LINK_SWITCH_NOTIFY_REASON_PRE_START_PRE_SER:
567 case MLO_LINK_SWITCH_NOTIFY_REASON_PRE_START_POST_SER:
568 if (!mlo_check_if_all_vdev_up(vdev)) {
569 mlo_debug("Not all VDEVs up");
570 return QDF_STATUS_E_AGAIN;
571 }
572
573 if (mlo_is_chan_switch_in_progress(vdev)) {
574 mlo_debug("CSA is in progress on one of ML vdevs, abort link switch");
575 return QDF_STATUS_E_AGAIN;
576 }
577
578 if (notify_reason ==
579 MLO_LINK_SWITCH_NOTIFY_REASON_PRE_START_PRE_SER) {
580 return QDF_STATUS_SUCCESS;
581 }
582
583 break;
584 default:
585 break;
586 }
587
588 status = mlo_mgr_link_switch_osif_notification(vdev, lswitch_req);
589
590 return status;
591 }
592
mlo_mgr_link_switch_init(struct wlan_objmgr_psoc * psoc,struct wlan_mlo_dev_context * ml_dev)593 QDF_STATUS mlo_mgr_link_switch_init(struct wlan_objmgr_psoc *psoc,
594 struct wlan_mlo_dev_context *ml_dev)
595 {
596 ml_dev->link_ctx =
597 qdf_mem_malloc(sizeof(struct mlo_link_switch_context));
598
599 if (!ml_dev->link_ctx)
600 return QDF_STATUS_E_NOMEM;
601
602 mlo_mgr_link_switch_init_state(ml_dev);
603 mlo_mgr_alloc_link_info_wmi_chan(ml_dev);
604 mlo_mgr_update_link_info_reset(psoc, ml_dev);
605
606 return QDF_STATUS_SUCCESS;
607 }
608
mlo_mgr_link_switch_deinit(struct wlan_mlo_dev_context * ml_dev)609 QDF_STATUS mlo_mgr_link_switch_deinit(struct wlan_mlo_dev_context *ml_dev)
610 {
611 mlo_mgr_free_link_info_wmi_chan(ml_dev);
612 qdf_mem_free(ml_dev->link_ctx);
613 ml_dev->link_ctx = NULL;
614 return QDF_STATUS_SUCCESS;
615 }
616
617 void
mlo_mgr_osif_update_connect_info(struct wlan_objmgr_vdev * vdev,int32_t link_id)618 mlo_mgr_osif_update_connect_info(struct wlan_objmgr_vdev *vdev, int32_t link_id)
619 {
620 struct mlo_mgr_context *g_mlo_ctx = wlan_objmgr_get_mlo_ctx();
621 struct mlo_link_info *link_info;
622 QDF_STATUS(*osif_bss_update_cb)(struct qdf_mac_addr *self_mac,
623 struct qdf_mac_addr *bssid,
624 int32_t link_id);
625
626 if (!g_mlo_ctx || !vdev->mlo_dev_ctx || !g_mlo_ctx->osif_ops ||
627 !g_mlo_ctx->osif_ops->mlo_mgr_osif_update_bss_info)
628 return;
629
630 link_info = mlo_mgr_get_ap_link_by_link_id(vdev->mlo_dev_ctx, link_id);
631 if (!link_info)
632 return;
633
634 mlo_debug("VDEV ID %d, Link ID %d, STA MAC " QDF_MAC_ADDR_FMT ", BSSID " QDF_MAC_ADDR_FMT,
635 link_info->vdev_id, link_id,
636 QDF_MAC_ADDR_REF(link_info->link_addr.bytes),
637 QDF_MAC_ADDR_REF(link_info->ap_link_addr.bytes));
638 osif_bss_update_cb = g_mlo_ctx->osif_ops->mlo_mgr_osif_update_bss_info;
639
640 osif_bss_update_cb(&link_info->link_addr, &link_info->ap_link_addr,
641 link_id);
642 }
643
mlo_mgr_link_switch_disconnect_done(struct wlan_objmgr_vdev * vdev,QDF_STATUS discon_status,bool is_link_switch_resp)644 QDF_STATUS mlo_mgr_link_switch_disconnect_done(struct wlan_objmgr_vdev *vdev,
645 QDF_STATUS discon_status,
646 bool is_link_switch_resp)
647 {
648 QDF_STATUS status;
649 enum mlo_link_switch_req_state cur_state;
650 struct mlo_link_info *new_link_info;
651 struct qdf_mac_addr mac_addr, mld_addr;
652 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx;
653 struct wlan_mlo_link_switch_req *req = &mlo_dev_ctx->link_ctx->last_req;
654
655 if (!is_link_switch_resp) {
656 mlo_mgr_link_switch_trans_abort_state(mlo_dev_ctx);
657 return QDF_STATUS_SUCCESS;
658 }
659
660 cur_state = mlo_mgr_link_switch_get_curr_state(mlo_dev_ctx);
661 if (QDF_IS_STATUS_ERROR(discon_status) ||
662 cur_state != MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK) {
663 mlo_err("VDEV %d link switch disconnect req failed",
664 req->vdev_id);
665 mlo_mgr_remove_link_switch_cmd(vdev);
666 return QDF_STATUS_SUCCESS;
667 }
668
669 mlo_debug("VDEV %d link switch disconnect complete",
670 wlan_vdev_get_id(vdev));
671
672 new_link_info =
673 mlo_mgr_get_ap_link_by_link_id(vdev->mlo_dev_ctx,
674 req->new_ieee_link_id);
675 if (!new_link_info) {
676 mlo_err("New link not found in mlo dev ctx");
677 mlo_mgr_remove_link_switch_cmd(vdev);
678 return QDF_STATUS_E_INVAL;
679 }
680
681 qdf_copy_macaddr(&mld_addr, &mlo_dev_ctx->mld_addr);
682 qdf_copy_macaddr(&mac_addr, &new_link_info->link_addr);
683
684 status = mlo_mgr_link_switch_trans_next_state(mlo_dev_ctx);
685 if (QDF_IS_STATUS_ERROR(status)) {
686 mlo_mgr_remove_link_switch_cmd(vdev);
687 return status;
688 }
689
690 status = wlan_vdev_mlme_send_set_mac_addr(mac_addr, mld_addr, vdev);
691 if (QDF_IS_STATUS_ERROR(status)) {
692 mlo_debug("VDEV %d set MAC addr FW request failed",
693 req->vdev_id);
694 mlo_mgr_remove_link_switch_cmd(vdev);
695 }
696
697 return status;
698 }
699
mlo_mgr_link_switch_set_mac_addr_resp(struct wlan_objmgr_vdev * vdev,uint8_t resp_status)700 QDF_STATUS mlo_mgr_link_switch_set_mac_addr_resp(struct wlan_objmgr_vdev *vdev,
701 uint8_t resp_status)
702 {
703 QDF_STATUS status = QDF_STATUS_E_INVAL;
704 enum mlo_link_switch_req_state cur_state;
705 struct mlo_mgr_context *g_mlo_ctx = wlan_objmgr_get_mlo_ctx();
706 struct wlan_mlo_link_switch_req *req;
707 struct mlo_link_info *new_link_info;
708
709 if (resp_status) {
710 mlo_err("VDEV %d set MAC address response %d",
711 wlan_vdev_get_id(vdev), resp_status);
712 mlo_mgr_remove_link_switch_cmd(vdev);
713 return status;
714 }
715
716 if (!g_mlo_ctx) {
717 mlo_err("global mlo ctx NULL");
718 mlo_mgr_remove_link_switch_cmd(vdev);
719 return status;
720 }
721
722 req = &vdev->mlo_dev_ctx->link_ctx->last_req;
723 cur_state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);
724 if (cur_state != MLO_LINK_SWITCH_STATE_SET_MAC_ADDR) {
725 mlo_err("Link switch cmd flushed, there can be MAC addr mismatch with FW");
726 mlo_mgr_remove_link_switch_cmd(vdev);
727 return status;
728 }
729
730 new_link_info =
731 mlo_mgr_get_ap_link_by_link_id(vdev->mlo_dev_ctx,
732 req->new_ieee_link_id);
733 if (!new_link_info) {
734 mlo_mgr_remove_link_switch_cmd(vdev);
735 return status;
736 }
737
738 wlan_vdev_mlme_set_macaddr(vdev, new_link_info->link_addr.bytes);
739 wlan_vdev_mlme_set_linkaddr(vdev, new_link_info->link_addr.bytes);
740
741 status = g_mlo_ctx->osif_ops->mlo_mgr_osif_update_mac_addr(
742 req->curr_ieee_link_id,
743 req->new_ieee_link_id,
744 req->vdev_id);
745 if (QDF_IS_STATUS_ERROR(status)) {
746 mlo_debug("VDEV %d OSIF MAC addr update failed %d",
747 req->vdev_id, status);
748 mlo_mgr_remove_link_switch_cmd(vdev);
749 return status;
750 }
751
752 status = mlo_mgr_link_switch_trans_next_state(vdev->mlo_dev_ctx);
753 if (QDF_IS_STATUS_ERROR(status)) {
754 mlo_mgr_remove_link_switch_cmd(vdev);
755 return status;
756 }
757
758 status = mlo_mgr_link_switch_start_connect(vdev);
759
760 return status;
761 }
762
mlo_mgr_link_switch_start_connect(struct wlan_objmgr_vdev * vdev)763 QDF_STATUS mlo_mgr_link_switch_start_connect(struct wlan_objmgr_vdev *vdev)
764 {
765 QDF_STATUS status = QDF_STATUS_E_INVAL;
766 struct wlan_cm_connect_req conn_req = {0};
767 struct mlo_link_info *mlo_link_info;
768 uint8_t *vdev_mac;
769 struct wlan_mlo_sta *sta_ctx;
770 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx;
771 struct wlan_mlo_link_switch_req *req = &mlo_dev_ctx->link_ctx->last_req;
772 struct wlan_objmgr_vdev *assoc_vdev = wlan_mlo_get_assoc_link_vdev(vdev);
773
774 if (!assoc_vdev) {
775 mlo_err("Assoc VDEV not found");
776 goto out;
777 }
778
779 mlo_link_info = mlo_mgr_get_ap_link_by_link_id(mlo_dev_ctx,
780 req->new_ieee_link_id);
781
782 if (!mlo_link_info) {
783 mlo_err("New link ID not found");
784 goto out;
785 }
786
787 vdev_mac = wlan_vdev_mlme_get_linkaddr(vdev);
788 if (!qdf_is_macaddr_equal(&mlo_link_info->link_addr,
789 (struct qdf_mac_addr *)vdev_mac)) {
790 mlo_err("MAC address not equal for the new Link ID VDEV: " QDF_MAC_ADDR_FMT ", MLO_LINK: " QDF_MAC_ADDR_FMT,
791 QDF_MAC_ADDR_REF(vdev_mac),
792 QDF_MAC_ADDR_REF(mlo_link_info->link_addr.bytes));
793 goto out;
794 }
795
796 sta_ctx = mlo_dev_ctx->sta_ctx;
797 copied_conn_req_lock_acquire(sta_ctx);
798 if (sta_ctx->copied_conn_req) {
799 qdf_mem_copy(&conn_req, sta_ctx->copied_conn_req,
800 sizeof(struct wlan_cm_connect_req));
801 } else {
802 copied_conn_req_lock_release(sta_ctx);
803 goto out;
804 }
805 copied_conn_req_lock_release(sta_ctx);
806
807 conn_req.vdev_id = wlan_vdev_get_id(vdev);
808 conn_req.source = CM_MLO_LINK_SWITCH_CONNECT;
809 wlan_vdev_set_link_id(vdev, req->new_ieee_link_id);
810
811 qdf_copy_macaddr(&conn_req.bssid, &mlo_link_info->ap_link_addr);
812 wlan_vdev_mlme_get_ssid(assoc_vdev, conn_req.ssid.ssid,
813 &conn_req.ssid.length);
814 status = wlan_vdev_get_bss_peer_mld_mac(assoc_vdev, &conn_req.mld_addr);
815 if (QDF_IS_STATUS_ERROR(status)) {
816 mlo_debug("Get MLD addr failed");
817 goto out;
818 }
819
820 conn_req.crypto.auth_type = 0;
821 conn_req.ml_parnter_info = sta_ctx->ml_partner_info;
822 mlo_allocate_and_copy_ies(&conn_req, sta_ctx->copied_conn_req);
823
824 status = wlan_cm_start_connect(vdev, &conn_req);
825 if (QDF_IS_STATUS_SUCCESS(status))
826 mlo_update_connected_links(vdev, 1);
827
828 wlan_cm_free_connect_req_param(&conn_req);
829
830 out:
831 if (QDF_IS_STATUS_ERROR(status)) {
832 mlo_err("VDEV %d link switch connect request failed",
833 wlan_vdev_get_id(vdev));
834 mlo_mgr_remove_link_switch_cmd(vdev);
835 }
836
837 return status;
838 }
839
840 static void
mlo_mgr_link_switch_connect_success_trans_state(struct wlan_objmgr_vdev * vdev)841 mlo_mgr_link_switch_connect_success_trans_state(struct wlan_objmgr_vdev *vdev)
842 {
843 enum mlo_link_switch_req_state curr_state;
844
845 /*
846 * If connection is success, then sending link switch failure to FW
847 * might result in not updating VDEV to link mapping in FW and FW may
848 * immediately send next link switch with params corresponding to
849 * pre-link switch which may vary post-link switch in host and might
850 * not be valid and results in Host-FW out-of-sync.
851 *
852 * Force the result of link switch in align with link switch connect
853 * so that Host and FW are not out of sync.
854 */
855 mlo_dev_lock_acquire(vdev->mlo_dev_ctx);
856 curr_state = vdev->mlo_dev_ctx->link_ctx->last_req.state;
857 vdev->mlo_dev_ctx->link_ctx->last_req.state =
858 MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS;
859 mlo_dev_lock_release(vdev->mlo_dev_ctx);
860
861 if (curr_state != MLO_LINK_SWITCH_STATE_CONNECT_NEW_LINK)
862 mlo_debug("Current link switch state %d changed", curr_state);
863 }
864
mlo_mgr_link_switch_connect_done(struct wlan_objmgr_vdev * vdev,QDF_STATUS status)865 void mlo_mgr_link_switch_connect_done(struct wlan_objmgr_vdev *vdev,
866 QDF_STATUS status)
867 {
868 struct wlan_mlo_link_switch_req *req;
869
870 req = &vdev->mlo_dev_ctx->link_ctx->last_req;
871 if (QDF_IS_STATUS_SUCCESS(status)) {
872 mlo_mgr_link_switch_connect_success_trans_state(vdev);
873 } else {
874 mlo_update_connected_links(vdev, 0);
875 mlo_err("VDEV %d link switch connect failed", req->vdev_id);
876 }
877
878 mlo_mgr_remove_link_switch_cmd(vdev);
879
880 if (QDF_IS_STATUS_ERROR(status))
881 mlo_mgr_restore_rso_upon_link_switch_failure(
882 wlan_mlo_get_assoc_link_vdev(vdev));
883 }
884
885 static enum wlan_mlo_link_switch_notify_reason
mlo_mgr_link_switch_get_notify_reason(struct wlan_objmgr_vdev * vdev)886 mlo_mgr_link_switch_get_notify_reason(struct wlan_objmgr_vdev *vdev)
887 {
888 enum mlo_link_switch_req_state curr_state;
889 enum wlan_mlo_link_switch_notify_reason notify_reason;
890
891 curr_state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);
892 switch (curr_state) {
893 case MLO_LINK_SWITCH_STATE_IDLE:
894 notify_reason = MLO_LINK_SWITCH_NOTIFY_REASON_PRE_START_PRE_SER;
895 break;
896 case MLO_LINK_SWITCH_STATE_INIT:
897 notify_reason =
898 MLO_LINK_SWITCH_NOTIFY_REASON_PRE_START_POST_SER;
899 break;
900 case MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS:
901 notify_reason = MLO_LINK_SWITCH_NOTIFY_REASON_STOP_SUCCESS;
902 break;
903 default:
904 notify_reason = MLO_LINK_SWITCH_NOTIFY_REASON_STOP_FAILURE;
905 break;
906 }
907
908 return notify_reason;
909 }
910
911 static QDF_STATUS
mlo_mgr_start_link_switch(struct wlan_objmgr_vdev * vdev,struct wlan_serialization_command * cmd)912 mlo_mgr_start_link_switch(struct wlan_objmgr_vdev *vdev,
913 struct wlan_serialization_command *cmd)
914 {
915 QDF_STATUS status = QDF_STATUS_E_INVAL;
916 uint8_t vdev_id, old_link_id, new_link_id;
917 struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx;
918 struct wlan_mlo_link_switch_req *req = &mlo_dev_ctx->link_ctx->last_req;
919 struct qdf_mac_addr bssid;
920
921 vdev_id = wlan_vdev_get_id(vdev);
922 old_link_id = req->curr_ieee_link_id;
923 new_link_id = req->new_ieee_link_id;
924
925 mlo_debug("VDEV %d start link switch", vdev_id);
926 mlo_mgr_link_switch_trans_next_state(mlo_dev_ctx);
927
928 if (!wlan_cm_is_vdev_connected(vdev)) {
929 mlo_err("VDEV %d not in connected state", vdev_id);
930 return status;
931 }
932
933 status = wlan_vdev_get_bss_peer_mac(vdev, &bssid);
934 if (QDF_IS_STATUS_ERROR(status))
935 return status;
936
937 status = wlan_vdev_get_bss_peer_mld_mac(vdev, &req->peer_mld_addr);
938 if (QDF_IS_STATUS_ERROR(status))
939 return status;
940
941 status = mlo_mgr_link_switch_notify(vdev, req);
942 if (QDF_IS_STATUS_ERROR(status))
943 return status;
944
945 wlan_vdev_mlme_set_mlo_link_switch_in_progress(vdev);
946 status = mlo_mgr_link_switch_trans_next_state(mlo_dev_ctx);
947 if (QDF_IS_STATUS_ERROR(status))
948 return status;
949
950 status = wlan_cm_disconnect(vdev, CM_MLO_LINK_SWITCH_DISCONNECT,
951 REASON_FW_TRIGGERED_LINK_SWITCH, &bssid);
952
953 if (QDF_IS_STATUS_ERROR(status))
954 mlo_err("VDEV %d disconnect request not handled", req->vdev_id);
955
956 return status;
957 }
958
959 static QDF_STATUS
mlo_mgr_ser_link_switch_cb(struct wlan_serialization_command * cmd,enum wlan_serialization_cb_reason cb_reason)960 mlo_mgr_ser_link_switch_cb(struct wlan_serialization_command *cmd,
961 enum wlan_serialization_cb_reason cb_reason)
962 {
963 struct wlan_objmgr_vdev *vdev;
964 QDF_STATUS status = QDF_STATUS_SUCCESS;
965 struct wlan_mlo_link_switch_req *req;
966 enum qdf_hang_reason reason = QDF_VDEV_ACTIVE_SER_LINK_SWITCH_TIMEOUT;
967
968 if (!cmd) {
969 mlo_err("cmd is NULL, reason: %d", cb_reason);
970 QDF_ASSERT(0);
971 return QDF_STATUS_E_NULL_VALUE;
972 }
973
974 vdev = cmd->vdev;
975 req = &vdev->mlo_dev_ctx->link_ctx->last_req;
976
977 switch (cb_reason) {
978 case WLAN_SER_CB_ACTIVATE_CMD:
979 status = mlo_mgr_start_link_switch(vdev, cmd);
980 if (QDF_IS_STATUS_ERROR(status)) {
981 mlo_mgr_link_switch_trans_abort_state(vdev->mlo_dev_ctx);
982 mlo_mgr_link_switch_notify(vdev, req);
983 }
984 break;
985 case WLAN_SER_CB_RELEASE_MEM_CMD:
986 mlo_mgr_link_switch_complete(vdev);
987 break;
988 case WLAN_SER_CB_CANCEL_CMD:
989 mlo_err("Link switch cmd cancelled");
990 break;
991 case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
992 mlo_err("Link switch active cmd timeout");
993 wlan_cm_trigger_panic_on_cmd_timeout(vdev, reason);
994 break;
995 default:
996 QDF_ASSERT(0);
997 mlo_mgr_link_switch_complete(vdev);
998 break;
999 }
1000
1001 return status;
1002 }
1003
mlo_mgr_remove_link_switch_cmd(struct wlan_objmgr_vdev * vdev)1004 void mlo_mgr_remove_link_switch_cmd(struct wlan_objmgr_vdev *vdev)
1005 {
1006 struct wlan_serialization_queued_cmd_info cmd_info;
1007 enum mlo_link_switch_req_state cur_state;
1008 uint8_t vdev_id = wlan_vdev_get_id(vdev);
1009 struct wlan_mlo_link_switch_req *req;
1010
1011 cur_state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);
1012 if (cur_state == MLO_LINK_SWITCH_STATE_IDLE)
1013 return;
1014
1015 req = &vdev->mlo_dev_ctx->link_ctx->last_req;
1016 mlo_mgr_link_switch_notify(vdev, req);
1017
1018 /* Force queue disconnect on failure */
1019 if (cur_state != MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS &&
1020 cur_state >= MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK &&
1021 !wlan_cm_is_vdev_connected(vdev)) {
1022 mlo_mgr_link_switch_defer_disconnect_req(vdev,
1023 CM_MLME_DISCONNECT,
1024 REASON_HOST_TRIGGERED_LINK_DELETE);
1025 }
1026
1027 /* Handle any pending disconnect */
1028 mlo_handle_pending_disconnect(vdev);
1029
1030 if (req->reason == MLO_LINK_SWITCH_REASON_HOST_FORCE) {
1031 mlo_debug("Link switch not serialized");
1032 mlo_mgr_link_switch_complete(vdev);
1033 return;
1034 }
1035
1036 cmd_info.cmd_id = (vdev_id << 16) + (req->new_ieee_link_id << 8) +
1037 (req->curr_ieee_link_id);
1038 cmd_info.req_type = WLAN_SER_CANCEL_NON_SCAN_CMD;
1039 cmd_info.cmd_type = WLAN_SER_CMD_MLO_VDEV_LINK_SWITCH;
1040 cmd_info.vdev = vdev;
1041 cmd_info.queue_type = WLAN_SERIALIZATION_ACTIVE_QUEUE;
1042
1043 wlan_serialization_remove_cmd(&cmd_info);
1044 }
1045
1046 #define MLO_MGR_MAX_LSWITCH_TIMEOUT 35000
1047
mlo_mgr_ser_link_switch_cmd(struct wlan_objmgr_vdev * vdev,struct wlan_mlo_link_switch_req * req)1048 QDF_STATUS mlo_mgr_ser_link_switch_cmd(struct wlan_objmgr_vdev *vdev,
1049 struct wlan_mlo_link_switch_req *req)
1050 {
1051 QDF_STATUS status;
1052 enum wlan_serialization_status ser_cmd_status;
1053 struct wlan_serialization_command cmd = {0};
1054 uint8_t vdev_id = wlan_vdev_get_id(vdev);
1055 struct mlo_link_switch_context *link_ctx;
1056
1057 if (!vdev->mlo_dev_ctx) {
1058 mlo_err("ML dev ctx NULL, reject link switch");
1059 return QDF_STATUS_E_INVAL;
1060 }
1061
1062 link_ctx = vdev->mlo_dev_ctx->link_ctx;
1063 link_ctx->last_req = *req;
1064
1065 cmd.cmd_type = WLAN_SER_CMD_MLO_VDEV_LINK_SWITCH;
1066 cmd.cmd_id = (vdev_id << 16) + (req->new_ieee_link_id << 8) +
1067 (req->curr_ieee_link_id);
1068 cmd.cmd_cb = mlo_mgr_ser_link_switch_cb;
1069 cmd.source = WLAN_UMAC_COMP_MLO_MGR;
1070 cmd.is_high_priority = false;
1071 cmd.cmd_timeout_duration = MLO_MGR_MAX_LSWITCH_TIMEOUT;
1072 cmd.vdev = vdev;
1073 cmd.is_blocking = true;
1074
1075 if (req->reason == MLO_LINK_SWITCH_REASON_HOST_FORCE) {
1076 mlo_debug("Do not serialize link switch");
1077 status = mlo_mgr_start_link_switch(vdev, &cmd);
1078 if (QDF_IS_STATUS_ERROR(status)) {
1079 mlo_mgr_link_switch_trans_abort_state(vdev->mlo_dev_ctx);
1080 mlo_mgr_link_switch_notify(vdev, req);
1081 }
1082 return status;
1083 }
1084
1085 ser_cmd_status = wlan_serialization_request(&cmd);
1086 switch (ser_cmd_status) {
1087 case WLAN_SER_CMD_PENDING:
1088 mlo_debug("Link switch cmd in pending queue");
1089 break;
1090 case WLAN_SER_CMD_ACTIVE:
1091 mlo_debug("Link switch cmd in active queue");
1092 break;
1093 default:
1094 return QDF_STATUS_E_INVAL;
1095 }
1096
1097 return QDF_STATUS_SUCCESS;
1098 }
1099
mlo_mgr_link_switch_notify(struct wlan_objmgr_vdev * vdev,struct wlan_mlo_link_switch_req * req)1100 QDF_STATUS mlo_mgr_link_switch_notify(struct wlan_objmgr_vdev *vdev,
1101 struct wlan_mlo_link_switch_req *req)
1102 {
1103 int8_t i;
1104 QDF_STATUS status, ret_status = QDF_STATUS_SUCCESS;
1105 enum wlan_mlo_link_switch_notify_reason notify_reason;
1106 struct mlo_mgr_context *mlo_mgr_ctx = wlan_objmgr_get_mlo_ctx();
1107
1108 if (!mlo_mgr_ctx) {
1109 mlo_err("Global mlo mgr NULL");
1110 return QDF_STATUS_E_NULL_VALUE;
1111 }
1112
1113 notify_reason = mlo_mgr_link_switch_get_notify_reason(vdev);
1114 for (i = 0; i < WLAN_UMAC_COMP_ID_MAX; i++) {
1115 if (!mlo_mgr_ctx->lswitch_notifier[i].in_use)
1116 continue;
1117
1118 status = mlo_mgr_ctx->lswitch_notifier[i].cb(vdev, req,
1119 notify_reason);
1120 if (QDF_IS_STATUS_SUCCESS(status))
1121 continue;
1122
1123 mlo_debug("Link switch notify %d failed in %d",
1124 notify_reason, i);
1125 ret_status = status;
1126 if (notify_reason == MLO_LINK_SWITCH_NOTIFY_REASON_PRE_START_PRE_SER)
1127 break;
1128 }
1129
1130 return ret_status;
1131 }
1132
1133 QDF_STATUS
mlo_mgr_link_switch_validate_request(struct wlan_objmgr_vdev * vdev,struct wlan_mlo_link_switch_req * req)1134 mlo_mgr_link_switch_validate_request(struct wlan_objmgr_vdev *vdev,
1135 struct wlan_mlo_link_switch_req *req)
1136 {
1137 QDF_STATUS status = QDF_STATUS_E_INVAL;
1138 uint8_t vdev_id = wlan_vdev_get_id(vdev);
1139 struct mlo_link_info *new_link_info;
1140
1141 if (req->curr_ieee_link_id >= WLAN_INVALID_LINK_ID ||
1142 req->new_ieee_link_id >= WLAN_INVALID_LINK_ID) {
1143 mlo_err("Invalid link params, curr link id %d, new link id %d",
1144 req->curr_ieee_link_id, req->new_ieee_link_id);
1145 return status;
1146 }
1147
1148 new_link_info = mlo_mgr_get_ap_link_by_link_id(vdev->mlo_dev_ctx,
1149 req->new_ieee_link_id);
1150 if (!new_link_info) {
1151 mlo_err("New link id %d not part of association",
1152 req->new_ieee_link_id);
1153 return status;
1154 }
1155
1156 if (new_link_info->vdev_id != WLAN_INVALID_VDEV_ID) {
1157 mlo_err("requested link already active on other vdev:%d",
1158 new_link_info->vdev_id);
1159 return status;
1160 }
1161
1162 if (!mlo_is_mld_sta(vdev)) {
1163 mlo_err("Link switch req not valid for VDEV %d", vdev_id);
1164 return status;
1165 }
1166
1167 if (!wlan_cm_is_vdev_connected(vdev)) {
1168 mlo_err("VDEV %d not in connected state", vdev_id);
1169 return status;
1170 }
1171
1172 if (mlo_mgr_is_link_switch_in_progress(vdev)) {
1173 mlo_err("Link switch already in progress");
1174 return status;
1175 }
1176
1177 if (wlan_vdev_get_link_id(vdev) != req->curr_ieee_link_id) {
1178 mlo_err("VDEV %d link id wrong, curr link id %d",
1179 vdev_id, wlan_vdev_get_link_id(vdev));
1180 return status;
1181 }
1182
1183 /* Notify callers on the new link switch request before serializing */
1184 status = mlo_mgr_link_switch_notify(vdev, req);
1185 if (QDF_IS_STATUS_ERROR(status)) {
1186 mlo_err("Link switch rejected in pre-serialize notify");
1187 return status;
1188 }
1189
1190 return QDF_STATUS_SUCCESS;
1191 }
1192
mlo_mgr_link_switch_request_params(struct wlan_objmgr_psoc * psoc,void * evt_params)1193 QDF_STATUS mlo_mgr_link_switch_request_params(struct wlan_objmgr_psoc *psoc,
1194 void *evt_params)
1195 {
1196 QDF_STATUS status;
1197 struct wlan_mlo_link_switch_cnf cnf_params = {0};
1198 struct wlan_mlo_link_switch_req *req;
1199 struct wlan_objmgr_vdev *vdev;
1200
1201 if (!evt_params) {
1202 mlo_err("Invalid params");
1203 return QDF_STATUS_E_INVAL;
1204 }
1205
1206 req = (struct wlan_mlo_link_switch_req *)evt_params;
1207
1208 /* The reference is released on Link Switch status confirm to FW */
1209 vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, req->vdev_id,
1210 WLAN_MLO_MGR_ID);
1211 if (!vdev) {
1212 mlo_err("Invalid link switch VDEV %d", req->vdev_id);
1213
1214 /* Fill reject params here and send to FW as VDEV is invalid */
1215 cnf_params.vdev_id = req->vdev_id;
1216 cnf_params.status = MLO_LINK_SWITCH_CNF_STATUS_REJECT;
1217 mlo_mgr_link_switch_send_cnf_cmd(psoc, &cnf_params);
1218 return QDF_STATUS_E_INVAL;
1219 }
1220
1221 mlo_debug("VDEV %d, curr_link_id %d, new_link_id %d, new_freq %d, new_phymode: %d, reason %d",
1222 req->vdev_id, req->curr_ieee_link_id, req->new_ieee_link_id,
1223 req->new_primary_freq, req->new_phymode, req->reason);
1224
1225 status = mlo_mgr_link_switch_validate_request(vdev, req);
1226 if (QDF_IS_STATUS_ERROR(status)) {
1227 mlo_debug("Link switch params/request invalid");
1228 mlo_mgr_link_switch_complete(vdev);
1229 return QDF_STATUS_E_INVAL;
1230 }
1231
1232 status = mlo_mgr_ser_link_switch_cmd(vdev, req);
1233 if (QDF_IS_STATUS_ERROR(status)) {
1234 mlo_err("Failed to serialize link switch command");
1235 mlo_mgr_link_switch_complete(vdev);
1236 }
1237
1238 return status;
1239 }
1240
1241 #define IS_LINK_SET(link_bitmap, link_id) ((link_bitmap) & (BIT(link_id)))
1242
mlo_mgr_update_link_state(struct wlan_mlo_dev_context * mld_ctx,uint32_t active_link_bitmap)1243 static void mlo_mgr_update_link_state(struct wlan_mlo_dev_context *mld_ctx,
1244 uint32_t active_link_bitmap)
1245 {
1246 uint8_t i;
1247 struct mlo_link_info *link_info;
1248
1249 for (i = 0; i < WLAN_MAX_ML_BSS_LINKS; i++) {
1250 link_info = &mld_ctx->link_ctx->links_info[i];
1251
1252 if (IS_LINK_SET(active_link_bitmap, link_info->link_id))
1253 link_info->is_link_active = true;
1254 else
1255 link_info->is_link_active = false;
1256 }
1257 }
1258
1259 QDF_STATUS
mlo_mgr_link_state_switch_info_handler(struct wlan_objmgr_psoc * psoc,struct mlo_link_switch_state_info * info)1260 mlo_mgr_link_state_switch_info_handler(struct wlan_objmgr_psoc *psoc,
1261 struct mlo_link_switch_state_info *info)
1262 {
1263 uint8_t i;
1264 struct wlan_mlo_dev_context *mld_ctx = NULL;
1265
1266 wlan_mlo_get_mlpeer_by_peer_mladdr(
1267 &info->link_switch_param[0].mld_addr, &mld_ctx);
1268
1269 if (!mld_ctx) {
1270 mlo_err("mlo dev ctx for mld_mac: " QDF_MAC_ADDR_FMT " not found",
1271 QDF_MAC_ADDR_REF(info->link_switch_param[0].mld_addr.bytes));
1272 return QDF_STATUS_E_INVAL;
1273 }
1274
1275 for (i = 0; i < info->num_params; i++) {
1276 wlan_connectivity_mld_link_status_event(
1277 psoc,
1278 &info->link_switch_param[i]);
1279 mlo_mgr_update_link_state(
1280 mld_ctx,
1281 info->link_switch_param[i].active_link_bitmap);
1282 }
1283
1284 return QDF_STATUS_SUCCESS;
1285 }
1286
mlo_mgr_link_switch_complete(struct wlan_objmgr_vdev * vdev)1287 QDF_STATUS mlo_mgr_link_switch_complete(struct wlan_objmgr_vdev *vdev)
1288 {
1289 enum mlo_link_switch_req_state state;
1290 struct wlan_mlo_link_switch_cnf params = {0};
1291 struct mlo_link_switch_context *link_ctx;
1292 struct wlan_mlo_link_switch_req *req;
1293 struct wlan_objmgr_psoc *psoc;
1294
1295 /* Not checking NULL value as reference is already taken for vdev */
1296 psoc = wlan_vdev_get_psoc(vdev);
1297
1298 link_ctx = vdev->mlo_dev_ctx->link_ctx;
1299 req = &link_ctx->last_req;
1300
1301 state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);
1302 if (state != MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS)
1303 params.status = MLO_LINK_SWITCH_CNF_STATUS_REJECT;
1304 else
1305 params.status = MLO_LINK_SWITCH_CNF_STATUS_ACCEPT;
1306
1307 params.vdev_id = wlan_vdev_get_id(vdev);
1308 params.reason = MLO_LINK_SWITCH_CNF_REASON_BSS_PARAMS_CHANGED;
1309
1310 mlo_mgr_link_switch_send_cnf_cmd(psoc, ¶ms);
1311
1312 mlo_mgr_link_switch_init_state(vdev->mlo_dev_ctx);
1313 wlan_vdev_mlme_clear_mlo_link_switch_in_progress(vdev);
1314 wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID);
1315 return QDF_STATUS_SUCCESS;
1316 }
1317
1318 QDF_STATUS
mlo_mgr_link_switch_send_cnf_cmd(struct wlan_objmgr_psoc * psoc,struct wlan_mlo_link_switch_cnf * cnf_params)1319 mlo_mgr_link_switch_send_cnf_cmd(struct wlan_objmgr_psoc *psoc,
1320 struct wlan_mlo_link_switch_cnf *cnf_params)
1321 {
1322 QDF_STATUS status;
1323 struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops;
1324
1325 mlo_debug("VDEV %d link switch completed, %s", cnf_params->vdev_id,
1326 (cnf_params->status == MLO_LINK_SWITCH_CNF_STATUS_ACCEPT) ?
1327 "success" : "fail");
1328
1329 mlo_tx_ops = &psoc->soc_cb.tx_ops->mlo_ops;
1330 if (!mlo_tx_ops || !mlo_tx_ops->send_mlo_link_switch_cnf_cmd) {
1331 mlo_err("handler is not registered");
1332 return QDF_STATUS_E_INVAL;
1333 }
1334
1335 status = mlo_tx_ops->send_mlo_link_switch_cnf_cmd(psoc, cnf_params);
1336 if (QDF_IS_STATUS_ERROR(status))
1337 mlo_err("Link switch status update to FW failed");
1338
1339 return status;
1340 }
1341
1342 QDF_STATUS
mlo_mgr_link_switch_defer_disconnect_req(struct wlan_objmgr_vdev * vdev,enum wlan_cm_source source,enum wlan_reason_code reason)1343 mlo_mgr_link_switch_defer_disconnect_req(struct wlan_objmgr_vdev *vdev,
1344 enum wlan_cm_source source,
1345 enum wlan_reason_code reason)
1346 {
1347 struct wlan_mlo_dev_context *mlo_dev_ctx;
1348 struct wlan_mlo_sta *sta_ctx;
1349
1350 if (!mlo_mgr_is_link_switch_in_progress(vdev)) {
1351 mlo_info("Link switch not in progress");
1352 return QDF_STATUS_E_INVAL;
1353 }
1354
1355 mlo_dev_ctx = vdev->mlo_dev_ctx;
1356 sta_ctx = mlo_dev_ctx->sta_ctx;
1357
1358 if (!sta_ctx) {
1359 mlo_err("sta ctx null");
1360 return QDF_STATUS_E_NULL_VALUE;
1361 }
1362
1363 /* Move current link switch to abort state */
1364 mlo_mgr_link_switch_trans_abort_state(mlo_dev_ctx);
1365
1366 if (sta_ctx->disconn_req) {
1367 mlo_debug("Pending disconnect from source %d, reason %d",
1368 sta_ctx->disconn_req->source,
1369 sta_ctx->disconn_req->reason_code);
1370 return QDF_STATUS_E_ALREADY;
1371 }
1372
1373 sta_ctx->disconn_req =
1374 qdf_mem_malloc(sizeof(struct wlan_cm_disconnect_req));
1375 if (!sta_ctx->disconn_req)
1376 return QDF_STATUS_E_NOMEM;
1377
1378 sta_ctx->disconn_req->vdev_id = wlan_vdev_get_id(vdev);
1379 sta_ctx->disconn_req->source = source;
1380 sta_ctx->disconn_req->reason_code = reason;
1381
1382 mlo_debug("Deferred disconnect source: %d, reason: %d", source, reason);
1383 return QDF_STATUS_SUCCESS;
1384 }
1385 #endif
1386