1 /*
2  * Copyright (c) 2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8 
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /**
19  * DOC : lim_mlo.c
20  *
21  * WLAN Host Device Driver file for 802.11be (Extremely High Throughput)
22  * support.
23  *
24  */
25 
26 #include "lim_mlo.h"
27 #include "sch_api.h"
28 #include "lim_types.h"
29 #include "wlan_mlo_mgr_ap.h"
30 #include "wlan_mlo_mgr_op.h"
31 #include <wlan_mlo_mgr_peer.h>
32 #include <lim_assoc_utils.h>
33 #include <wlan_mlo_mgr_peer.h>
34 #include <lim_utils.h>
35 #include <utils_mlo.h>
36 
lim_cu_info_from_rnr_per_link_id(const uint8_t * rnr,uint8_t linkid,uint8_t * bpcc,uint8_t * aui)37 QDF_STATUS lim_cu_info_from_rnr_per_link_id(const uint8_t *rnr,
38 					    uint8_t linkid, uint8_t *bpcc,
39 					    uint8_t *aui)
40 {
41 	const uint8_t *data, *rnr_end;
42 	struct neighbor_ap_info_field *neighbor_ap_info;
43 	uint8_t tbtt_type, tbtt_len, tbtt_count;
44 	uint8_t mld_pos, mld_id, link_id;
45 	struct rnr_mld_info *mld_param;
46 	int32_t i, len;
47 	uint8_t nbr_ap_info_len = sizeof(struct neighbor_ap_info_field);
48 
49 	if (!rnr)
50 		return QDF_STATUS_E_INVAL;
51 
52 	rnr_end = rnr + rnr[TAG_LEN_POS] + MIN_IE_LEN;
53 	data = rnr + PAYLOAD_START_POS;
54 	while ((data + sizeof(struct neighbor_ap_info_field)) <= rnr_end) {
55 		neighbor_ap_info = (struct neighbor_ap_info_field *)data;
56 		tbtt_count = neighbor_ap_info->tbtt_header.tbtt_info_count;
57 		tbtt_len = neighbor_ap_info->tbtt_header.tbtt_info_length;
58 		tbtt_type = neighbor_ap_info->tbtt_header.tbbt_info_fieldtype;
59 		len = tbtt_len * (tbtt_count + 1) + nbr_ap_info_len;
60 		if (data + len > rnr_end) {
61 			pe_debug("error about rnr length");
62 			return QDF_STATUS_E_INVAL;
63 		}
64 
65 		if (tbtt_len >=
66 		    TBTT_NEIGHBOR_AP_BSSID_S_SSID_BSS_PARAM_20MHZ_PSD_MLD_PARAM)
67 			mld_pos =
68 			      TBTT_NEIGHBOR_AP_BSSID_S_SSID_BSS_PARAM_20MHZ_PSD;
69 		else
70 			mld_pos = 0;
71 
72 		if (mld_pos == 0 || tbtt_type != 0) {
73 			data += len;
74 			continue;
75 		}
76 
77 		data += nbr_ap_info_len;
78 		for (i = 0; i < tbtt_count + 1; i++) {
79 			mld_param = (struct rnr_mld_info *)&data[mld_pos];
80 			mld_id = mld_param->mld_id;
81 			if (mld_id == 0) {
82 				link_id = mld_param->link_id;
83 				if (linkid == link_id) {
84 					*bpcc = mld_param->bss_param_change_cnt;
85 					*aui = mld_param->all_updates_included;
86 					pe_debug("rnr bpcc %d, aui %d, linkid %d",
87 						 *bpcc, *aui, linkid);
88 					return QDF_STATUS_SUCCESS;
89 				}
90 			}
91 			data += tbtt_len;
92 		}
93 	}
94 
95 	return QDF_STATUS_E_INVAL;
96 }
97 
lim_get_bpcc_from_mlo_ie(tSchBeaconStruct * bcn,uint8_t * bpcc)98 QDF_STATUS lim_get_bpcc_from_mlo_ie(tSchBeaconStruct *bcn, uint8_t *bpcc)
99 {
100 	struct sir_multi_link_ie *mlo_ie;
101 	QDF_STATUS status = QDF_STATUS_E_INVAL;
102 
103 	if (!bcn)
104 		return status;
105 
106 	mlo_ie = &bcn->mlo_ie;
107 	if (mlo_ie->mlo_ie_present &&
108 	    mlo_ie->mlo_ie.bss_param_change_cnt_present) {
109 		*bpcc = mlo_ie->mlo_ie.bss_param_change_count;
110 		pe_debug("mlie bpcc %d", *bpcc);
111 		status = QDF_STATUS_SUCCESS;
112 	} else {
113 		*bpcc = 0;
114 	}
115 
116 	return status;
117 }
118 
lim_check_cu_happens(struct wlan_objmgr_vdev * vdev,uint8_t new_bpcc)119 bool lim_check_cu_happens(struct wlan_objmgr_vdev *vdev, uint8_t new_bpcc)
120 {
121 	uint8_t bpcc;
122 	uint8_t vdev_id;
123 	QDF_STATUS status;
124 
125 	if (!vdev || !wlan_vdev_mlme_is_mlo_vdev(vdev))
126 		return false;
127 
128 	vdev_id = wlan_vdev_get_id(vdev);
129 
130 	status = wlan_mlo_get_cu_bpcc(vdev, &bpcc);
131 	if (QDF_IS_STATUS_ERROR(status))
132 		return false;
133 
134 	if (new_bpcc == 0 && bpcc == 0)
135 		return false;
136 
137 	pe_debug_rl("vdev id %d new bpcc %d, old bpcc %d",
138 		    vdev_id, new_bpcc, bpcc);
139 	if (new_bpcc && new_bpcc < bpcc)
140 		return false;
141 
142 	wlan_mlo_set_cu_bpcc(vdev, new_bpcc);
143 
144 	return true;
145 }
146 
147 /**
148  * lim_send_mlo_ie_update() - mlo ie is changed, populate new beacon template
149  * @session: pe session
150  *
151  * Return: void
152  */
lim_send_mlo_ie_update(struct mac_context * mac_ctx,struct pe_session * session)153 static void lim_send_mlo_ie_update(struct mac_context *mac_ctx,
154 				   struct pe_session *session)
155 {
156 	if (QDF_IS_STATUS_ERROR(
157 		sch_set_fixed_beacon_fields(mac_ctx, session))) {
158 		pe_err("Unable to update mlo IE in beacon");
159 		return;
160 	}
161 
162 	lim_send_beacon_ind(mac_ctx, session, REASON_MLO_IE_UPDATE);
163 }
164 
lim_partner_link_info_change(struct wlan_objmgr_vdev * vdev)165 QDF_STATUS lim_partner_link_info_change(struct wlan_objmgr_vdev *vdev)
166 {
167 	struct pe_session *session;
168 	struct mac_context *mac;
169 
170 	mac = cds_get_context(QDF_MODULE_ID_PE);
171 	if (!mac) {
172 		pe_err("mac ctx is null");
173 		return QDF_STATUS_E_INVAL;
174 	}
175 	if (!vdev) {
176 		pe_err("vdev is null");
177 		return QDF_STATUS_E_INVAL;
178 	}
179 	session = pe_find_session_by_vdev_id(
180 			mac, vdev->vdev_objmgr.vdev_id);
181 	if (!session) {
182 		pe_err("session is NULL");
183 		return QDF_STATUS_E_INVAL;
184 	}
185 
186 	if (session->mlo_link_info.bcn_tmpl_exist)
187 		lim_send_mlo_ie_update(mac, session);
188 
189 	return QDF_STATUS_SUCCESS;
190 }
191 
lim_mlo_release_vdev_ref(struct wlan_objmgr_vdev * vdev)192 void lim_mlo_release_vdev_ref(struct wlan_objmgr_vdev *vdev)
193 {
194 	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID);
195 }
196 
pe_find_partner_session_by_link_id(struct pe_session * session,uint8_t link_id)197 struct pe_session *pe_find_partner_session_by_link_id(
198 			struct pe_session *session, uint8_t link_id)
199 {
200 	struct wlan_objmgr_vdev *vdev;
201 	struct mac_context *mac;
202 	struct pe_session *partner_session;
203 
204 	mac = cds_get_context(QDF_MODULE_ID_PE);
205 	if (!mac) {
206 		pe_err("mac ctx is null");
207 		return NULL;
208 	}
209 
210 	if (!session) {
211 		pe_err("session is null");
212 		return NULL;
213 	}
214 
215 	vdev = mlo_get_vdev_by_link_id(session->vdev, link_id,
216 				       WLAN_LEGACY_MAC_ID);
217 
218 	if (!vdev) {
219 		pe_err("vdev is null");
220 		return NULL;
221 	}
222 
223 	partner_session = pe_find_session_by_vdev_id(
224 			mac, vdev->vdev_objmgr.vdev_id);
225 
226 	if (!partner_session)
227 		wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID);
228 
229 	return partner_session;
230 }
231 
lim_get_mlo_vdev_list(struct pe_session * session,uint16_t * vdev_count,struct wlan_objmgr_vdev ** wlan_vdev_list)232 void lim_get_mlo_vdev_list(struct pe_session *session, uint16_t *vdev_count,
233 			   struct wlan_objmgr_vdev **wlan_vdev_list)
234 {
235 	mlo_ap_get_vdev_list(session->vdev, vdev_count,
236 			     wlan_vdev_list);
237 }
238 
239 /**
240  * lim_mlo_get_assoc_link_session_sta_ds() - get assoc link session and sta ds
241  * @session: pe session
242  * @partner_peer_idx: aid
243  * @assoc_session: assoc link session
244  * @assoc_sta: assoc sta ds
245  *
246  * Return: void
247  */
lim_mlo_get_assoc_link_session_sta_ds(struct pe_session * session,uint16_t partner_peer_idx,struct pe_session ** assoc_session,tpDphHashNode * assoc_sta)248 static void lim_mlo_get_assoc_link_session_sta_ds(
249 				struct pe_session *session,
250 				uint16_t partner_peer_idx,
251 				struct pe_session **assoc_session,
252 				tpDphHashNode *assoc_sta)
253 {
254 	struct wlan_mlo_peer_context *mlo_peer_ctx;
255 	struct wlan_objmgr_peer *peer;
256 	uint16_t aid = 0;
257 	struct mac_context *mac;
258 	struct wlan_objmgr_vdev *vdev;
259 	struct pe_session *partner_session;
260 
261 	*assoc_session = NULL;
262 	*assoc_sta = NULL;
263 	mac = cds_get_context(QDF_MODULE_ID_PE);
264 	if (!mac) {
265 		pe_err("mac ctx is null");
266 		return;
267 	}
268 	if (!session) {
269 		pe_err("session is NULL");
270 		return;
271 	}
272 
273 	mlo_peer_ctx = wlan_mlo_get_mlpeer_by_aid(session->vdev->mlo_dev_ctx,
274 						  partner_peer_idx);
275 	if (!mlo_peer_ctx) {
276 		pe_err("mlo peer ctx is null");
277 		return;
278 	}
279 	peer = wlan_mlo_peer_get_assoc_peer(mlo_peer_ctx);
280 	if (!peer) {
281 		pe_err("peer is null");
282 		return;
283 	}
284 	vdev = wlan_peer_get_vdev(peer);
285 	if (!vdev) {
286 		pe_err("vdev is null");
287 		return;
288 	}
289 	partner_session = pe_find_session_by_vdev_id(
290 				mac, vdev->vdev_objmgr.vdev_id);
291 
292 	if (!partner_session) {
293 		pe_err("assoc session is null");
294 		return;
295 	}
296 	*assoc_sta = dph_lookup_hash_entry(mac, peer->macaddr, &aid,
297 					   &partner_session->dph.dphHashTable);
298 	*assoc_session = partner_session;
299 }
300 
301 /**
302  * lim_mlo_update_cleanup_trigger () - update clean up trigger
303  * @session: pointer to session
304  * @sta_ds: sta ds
305  * @clnup_tri: clean up trigger
306  *
307  * Return: Void
308  */
lim_mlo_update_cleanup_trigger(struct pe_session * session,tpDphHashNode sta_ds,uint16_t clnup_tri)309 static void lim_mlo_update_cleanup_trigger(struct pe_session *session,
310 					   tpDphHashNode sta_ds,
311 					   uint16_t clnup_tri)
312 {
313 	tpDphHashNode assoc_sta = NULL;
314 	struct pe_session *link_session;
315 	struct pe_session *assoc_session = NULL;
316 	struct mac_context *mac_ctx;
317 	tpDphHashNode link_sta;
318 	uint8_t link_id;
319 	int link;
320 	uint8_t *sta_addr;
321 	uint16_t assoc_id;
322 
323 	mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
324 	if (!mac_ctx) {
325 		pe_err("mac ctx is null");
326 		return;
327 	}
328 	if (!session) {
329 		pe_err("session is null");
330 		return;
331 	}
332 	if (!sta_ds) {
333 		pe_err("sta ds is null");
334 		return;
335 	}
336 
337 	if (lim_is_mlo_recv_assoc(sta_ds)) {
338 		assoc_sta = sta_ds;
339 	} else {
340 		lim_mlo_get_assoc_link_session_sta_ds(session, sta_ds->assocId,
341 						      &assoc_session,
342 						      &assoc_sta);
343 		if (!assoc_sta) {
344 			pe_err("assoc link sta ds is null");
345 			return;
346 		}
347 
348 		assoc_sta->mlmStaContext.cleanupTrigger = clnup_tri;
349 	}
350 	for (link = 0; link < assoc_sta->mlo_info.num_partner_links; link++) {
351 		link_id = assoc_sta->mlo_info.partner_link_info[link].link_id;
352 		link_session = pe_find_partner_session_by_link_id(session,
353 								  link_id);
354 		if (!link_session)
355 			continue;
356 		sta_addr =
357 		    assoc_sta->mlo_info.partner_link_info[link].link_addr.bytes;
358 		link_sta = dph_lookup_hash_entry(
359 				mac_ctx,
360 				sta_addr,
361 				&assoc_id,
362 				&link_session->dph.dphHashTable);
363 		if (!link_sta || link_sta == sta_ds) {
364 			lim_mlo_release_vdev_ref(link_session->vdev);
365 			continue;
366 		}
367 		link_sta->mlmStaContext.cleanupTrigger = clnup_tri;
368 		lim_mlo_release_vdev_ref(link_session->vdev);
369 	}
370 }
371 
lim_mlo_notify_peer_disconn(struct pe_session * pe_session,tpDphHashNode sta_ds)372 void lim_mlo_notify_peer_disconn(struct pe_session *pe_session,
373 				 tpDphHashNode sta_ds)
374 {
375 	struct wlan_objmgr_peer *peer;
376 	struct mac_context *mac_ctx;
377 
378 	if (!pe_session) {
379 		pe_err("pe session is null");
380 		return;
381 	}
382 	if (!sta_ds) {
383 		pe_err("sta ds is null");
384 		return;
385 	}
386 	mac_ctx = pe_session->mac_ctx;
387 	if (!mac_ctx) {
388 		pe_err("mac context is null");
389 		return;
390 	}
391 
392 	peer = wlan_objmgr_get_peer_by_mac(mac_ctx->psoc,
393 					   sta_ds->staAddr,
394 					   WLAN_LEGACY_MAC_ID);
395 	if (!peer) {
396 		pe_err("peer is null");
397 		return;
398 	}
399 
400 	if (wlan_peer_mlme_flag_ext_get(peer, WLAN_PEER_FEXT_MLO)) {
401 		if (wlan_vdev_mlme_is_mlo_ap(pe_session->vdev))
402 			lim_mlo_update_cleanup_trigger(
403 					pe_session, sta_ds,
404 					sta_ds->mlmStaContext.cleanupTrigger);
405 		wlan_mlo_partner_peer_disconnect_notify(peer);
406 	}
407 
408 	wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_MAC_ID);
409 }
410 
lim_mlo_sta_notify_peer_disconn(struct pe_session * pe_session)411 void lim_mlo_sta_notify_peer_disconn(struct pe_session *pe_session)
412 {
413 	struct wlan_objmgr_peer *peer;
414 	struct mac_context *mac_ctx;
415 
416 	if (!pe_session) {
417 		pe_err("pe session is null");
418 		return;
419 	}
420 	mac_ctx = pe_session->mac_ctx;
421 	if (!mac_ctx) {
422 		pe_err("mac context is null");
423 		return;
424 	}
425 
426 	peer = wlan_objmgr_get_peer_by_mac(mac_ctx->psoc,
427 					   pe_session->bssId,
428 					   WLAN_LEGACY_MAC_ID);
429 	if (!peer) {
430 		pe_err("peer is null");
431 		return;
432 	}
433 
434 	if (wlan_peer_mlme_flag_ext_get(peer, WLAN_PEER_FEXT_MLO))
435 		wlan_mlo_partner_peer_disconnect_notify(peer);
436 
437 	wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_MAC_ID);
438 }
439 
lim_mlo_roam_peer_disconn_del(struct wlan_objmgr_vdev * vdev)440 void lim_mlo_roam_peer_disconn_del(struct wlan_objmgr_vdev *vdev)
441 {
442 	struct wlan_objmgr_peer *peer;
443 	struct wlan_objmgr_psoc *psoc;
444 	QDF_STATUS status = QDF_STATUS_SUCCESS;
445 	struct qdf_mac_addr bssid;
446 
447 	if (!vdev) {
448 		pe_err("vdev is null");
449 		return;
450 	}
451 
452 	psoc = wlan_vdev_get_psoc(vdev);
453 	if (!psoc) {
454 		pe_err("psoc is null");
455 		return;
456 	}
457 
458 	status = wlan_vdev_get_bss_peer_mac(vdev, &bssid);
459 	if (QDF_IS_STATUS_ERROR(status)) {
460 		pe_debug("vdev id %d : failed to get bssid",
461 			 wlan_vdev_get_id(vdev));
462 		return;
463 	}
464 
465 	peer = wlan_objmgr_get_peer_by_mac(psoc,
466 					   bssid.bytes,
467 					   WLAN_LEGACY_MAC_ID);
468 	if (!peer) {
469 		pe_err("peer is null");
470 		return;
471 	}
472 
473 	if (wlan_peer_mlme_flag_ext_get(peer, WLAN_PEER_FEXT_MLO)) {
474 		pe_debug("vdev id %d disconn del peer", wlan_vdev_get_id(vdev));
475 		wlan_mlo_partner_peer_disconnect_notify(peer);
476 		wlan_mlo_link_peer_delete(peer);
477 	}
478 
479 	wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_MAC_ID);
480 }
481 
lim_mlo_cleanup_partner_peer(struct wlan_objmgr_peer * peer)482 void lim_mlo_cleanup_partner_peer(struct wlan_objmgr_peer *peer)
483 {
484 	struct mac_context *mac_ctx;
485 	uint16_t aid;
486 	tpDphHashNode sta_ds;
487 	struct pe_session *pe_session;
488 	tpSirAssocReq tmp_assoc_req;
489 	struct wlan_objmgr_vdev *vdev;
490 
491 	mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
492 	if (!mac_ctx) {
493 		pe_err("mac ctx is null");
494 		return;
495 	}
496 
497 	vdev = wlan_peer_get_vdev(peer);
498 	if (!vdev) {
499 		pe_err("vdev is null");
500 		return;
501 	}
502 
503 	pe_session = pe_find_session_by_vdev_id(
504 			mac_ctx, vdev->vdev_objmgr.vdev_id);
505 	if (!pe_session) {
506 		pe_err("pe session is null");
507 		return;
508 	}
509 
510 	sta_ds = dph_lookup_hash_entry(mac_ctx, peer->macaddr, &aid,
511 				       &pe_session->dph.dphHashTable);
512 
513 	if (!sta_ds) {
514 		pe_err("sta ds is null");
515 		return;
516 	}
517 
518 	lim_cleanup_rx_path(mac_ctx, sta_ds, pe_session, true);
519 
520 	if (pe_session->parsedAssocReq) {
521 		tmp_assoc_req = pe_session->parsedAssocReq[sta_ds->assocId];
522 		if (tmp_assoc_req) {
523 			lim_free_assoc_req_frm_buf(tmp_assoc_req);
524 			qdf_mem_free(tmp_assoc_req);
525 			tmp_assoc_req = NULL;
526 		}
527 
528 		pe_session->parsedAssocReq[sta_ds->assocId] = NULL;
529 	}
530 }
531 
lim_mlo_set_mld_mac_peer(tpDphHashNode sta_ds,uint8_t peer_mld_addr[QDF_MAC_ADDR_SIZE])532 void lim_mlo_set_mld_mac_peer(tpDphHashNode sta_ds,
533 			      uint8_t peer_mld_addr[QDF_MAC_ADDR_SIZE])
534 {
535 	WLAN_ADDR_COPY(sta_ds->mld_addr, peer_mld_addr);
536 }
537 
lim_is_mlo_conn(struct pe_session * session,tpDphHashNode sta_ds)538 bool lim_is_mlo_conn(struct pe_session *session, tpDphHashNode sta_ds)
539 {
540 	bool mlo_conn = false;
541 
542 	if (!sta_ds) {
543 		pe_err("sta ds is null");
544 		return mlo_conn;
545 	}
546 
547 	if (!session) {
548 		pe_err("session is null");
549 		return mlo_conn;
550 	}
551 
552 	if (wlan_vdev_mlme_is_mlo_vdev(session->vdev) &&
553 	    !qdf_is_macaddr_zero((struct qdf_mac_addr *)sta_ds->mld_addr))
554 		mlo_conn = true;
555 
556 	return mlo_conn;
557 }
558 
lim_set_mlo_recv_assoc(tpDphHashNode sta_ds,bool mlo_recv_assoc_frm)559 void lim_set_mlo_recv_assoc(tpDphHashNode sta_ds, bool mlo_recv_assoc_frm)
560 {
561 	if (!sta_ds) {
562 		pe_err("sta ds is null");
563 		return;
564 	}
565 
566 	sta_ds->recv_assoc_frm = mlo_recv_assoc_frm;
567 }
568 
lim_is_mlo_recv_assoc(tpDphHashNode sta_ds)569 bool lim_is_mlo_recv_assoc(tpDphHashNode sta_ds)
570 {
571 	if (!sta_ds) {
572 		pe_err("sta ds is null");
573 		return false;
574 	}
575 
576 	return sta_ds->recv_assoc_frm;
577 }
578 
lim_mlo_proc_assoc_req_frm(struct wlan_objmgr_vdev * vdev,struct wlan_mlo_peer_context * ml_peer,struct qdf_mac_addr * link_addr,qdf_nbuf_t buf)579 QDF_STATUS lim_mlo_proc_assoc_req_frm(struct wlan_objmgr_vdev *vdev,
580 				      struct wlan_mlo_peer_context *ml_peer,
581 				      struct qdf_mac_addr *link_addr,
582 				      qdf_nbuf_t buf)
583 {
584 	struct mac_context *mac_ctx;
585 	struct pe_session *session;
586 	tSirMacAddr sa;
587 	uint8_t sub_type;
588 	uint32_t frame_len;
589 	uint8_t *frm_body;
590 	tpSirMacMgmtHdr pHdr;
591 	tSirMacFrameCtl fc;
592 	tpSirAssocReq assoc_req;
593 	QDF_STATUS status;
594 	qdf_size_t link_frame_len = 0;
595 	struct qdf_mac_addr link_bssid;
596 
597 	if (!vdev) {
598 		pe_err("vdev is null");
599 		return QDF_STATUS_E_INVAL;
600 	}
601 
602 	if (!ml_peer) {
603 		pe_err("ml_peer is null");
604 		return QDF_STATUS_E_INVAL;
605 	}
606 
607 	if (!link_addr) {
608 		pe_err("link addr is null");
609 		return QDF_STATUS_E_INVAL;
610 	}
611 
612 	if (!buf) {
613 		pe_err("assoq req buf is null");
614 		return QDF_STATUS_E_INVAL;
615 	}
616 
617 	mac_ctx = cds_get_context(QDF_MODULE_ID_PE);
618 	if (!mac_ctx) {
619 		pe_err("mac ctx is null");
620 		return QDF_STATUS_E_INVAL;
621 	}
622 
623 	session = pe_find_session_by_vdev_id(
624 			mac_ctx, vdev->vdev_objmgr.vdev_id);
625 	if (!session) {
626 		pe_err("session is NULL");
627 		return QDF_STATUS_E_INVAL;
628 	}
629 
630 	if (qdf_nbuf_len(buf) <= sizeof(*pHdr)) {
631 		pe_err("invalid buf");
632 		return QDF_STATUS_E_INVAL;
633 	}
634 
635 	frame_len = qdf_nbuf_len(buf) - sizeof(*pHdr);
636 	frm_body = qdf_nbuf_data(buf) + sizeof(*pHdr);
637 	pHdr = (tpSirMacMgmtHdr)qdf_nbuf_data(buf);
638 	fc = pHdr->fc;
639 
640 	if (fc.type == SIR_MAC_MGMT_FRAME) {
641 		if (fc.subType == SIR_MAC_MGMT_ASSOC_REQ) {
642 			sub_type = LIM_ASSOC;
643 		} else if (fc.subType == SIR_MAC_MGMT_REASSOC_REQ) {
644 			sub_type = LIM_REASSOC;
645 		} else {
646 			pe_err("invalid mgt_type %d, sub_type %d",
647 			       fc.type, fc.subType);
648 			return QDF_STATUS_E_INVAL;
649 		}
650 	} else {
651 		pe_err("invalid mgt_type %d, sub_type %d",
652 		       fc.type, fc.subType);
653 		return QDF_STATUS_E_INVAL;
654 	}
655 
656 	qdf_mem_copy(sa, link_addr->bytes, QDF_MAC_ADDR_SIZE);
657 	status = lim_check_assoc_req(mac_ctx, sub_type, sa, session);
658 	if (QDF_IS_STATUS_ERROR(status))
659 		return status;
660 
661 	/* Allocate memory for the Assoc Request frame */
662 	assoc_req = qdf_mem_malloc(sizeof(*assoc_req));
663 	if (!assoc_req)
664 		return QDF_STATUS_E_NOMEM;
665 
666 	assoc_req->assoc_req_buf = qdf_nbuf_copy(buf);
667 	if (!assoc_req->assoc_req_buf) {
668 		pe_err("partner link assoc request buf clone failed");
669 		qdf_mem_free(assoc_req);
670 		return QDF_STATUS_E_NOMEM;
671 	}
672 	qdf_copy_macaddr(&link_bssid, (struct qdf_mac_addr *)session->bssId);
673 	status = util_gen_link_assoc_req(
674 				frm_body, frame_len, sub_type == LIM_REASSOC,
675 				0,
676 				link_bssid,
677 				qdf_nbuf_data(assoc_req->assoc_req_buf),
678 				qdf_nbuf_len(assoc_req->assoc_req_buf),
679 				&link_frame_len);
680 	if (QDF_IS_STATUS_ERROR(status)) {
681 		pe_warn("Partner Assoc Req frame gen error. source addr:"
682 			QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(sa));
683 		lim_free_assoc_req_frm_buf(assoc_req);
684 		qdf_mem_free(assoc_req);
685 		return status;
686 	}
687 
688 	qdf_nbuf_set_len(assoc_req->assoc_req_buf, link_frame_len);
689 	assoc_req->assocReqFrame = qdf_nbuf_data(assoc_req->assoc_req_buf) +
690 				   sizeof(*pHdr);
691 	assoc_req->assocReqFrameLength = link_frame_len - sizeof(*pHdr);
692 
693 	qdf_copy_macaddr((struct qdf_mac_addr *)assoc_req->mld_mac,
694 			 &ml_peer->peer_mld_addr);
695 	return lim_proc_assoc_req_frm_cmn(mac_ctx, sub_type, session, sa,
696 					  assoc_req, ml_peer->assoc_id);
697 }
698 
lim_mlo_ap_sta_assoc_suc(struct wlan_objmgr_peer * peer)699 void lim_mlo_ap_sta_assoc_suc(struct wlan_objmgr_peer *peer)
700 {
701 	struct mac_context *mac;
702 	tpDphHashNode sta;
703 	struct pe_session *pe_session;
704 	struct wlan_objmgr_vdev *vdev;
705 	uint16_t aid = 0;
706 
707 	mac = cds_get_context(QDF_MODULE_ID_PE);
708 	if (!mac) {
709 		pe_err("mac ctx is null");
710 		return;
711 	}
712 	if (!peer) {
713 		pe_err("peer is null");
714 		return;
715 	}
716 	vdev = wlan_peer_get_vdev(peer);
717 
718 	pe_session = pe_find_session_by_vdev_id(
719 			mac, vdev->vdev_objmgr.vdev_id);
720 
721 	if (!pe_session) {
722 		pe_err("pe_session is NULL");
723 		return;
724 	}
725 	sta = dph_lookup_hash_entry(mac, peer->macaddr, &aid,
726 				    &pe_session->dph.dphHashTable);
727 	if (!sta) {
728 		pe_err("sta ds is null");
729 		return;
730 	}
731 	if (lim_send_mlm_assoc_ind(mac, sta, pe_session) != QDF_STATUS_SUCCESS)
732 		lim_reject_association(mac, sta->staAddr,
733 				       sta->mlmStaContext.subType,
734 				       true, sta->mlmStaContext.authType,
735 				       sta->assocId, true,
736 				       STATUS_UNSPECIFIED_FAILURE,
737 				       pe_session);
738 }
739 
lim_ap_mlo_sta_peer_ind(struct mac_context * mac,struct pe_session * pe_session,tpDphHashNode sta,bool add_sta_rsp_status)740 void lim_ap_mlo_sta_peer_ind(struct mac_context *mac,
741 			     struct pe_session *pe_session,
742 			     tpDphHashNode sta,
743 			     bool add_sta_rsp_status)
744 {
745 	tpSirAssocReq assoc_req;
746 	struct wlan_mlo_peer_context *ml_peer;
747 	struct wlan_objmgr_peer *peer;
748 	struct mlo_partner_info info;
749 	struct mlo_link_info *linfo;
750 
751 	if (!sta) {
752 		pe_err("sta ds is null");
753 		return;
754 	}
755 	if (add_sta_rsp_status) {
756 		peer = wlan_objmgr_get_peer_by_mac(mac->psoc,
757 						   sta->staAddr,
758 						   WLAN_LEGACY_MAC_ID);
759 		if (!peer) {
760 			pe_err("peer is null");
761 			return;
762 		}
763 
764 		if (lim_is_mlo_recv_assoc(sta)) {
765 			assoc_req = pe_session->parsedAssocReq[sta->assocId];
766 			if (assoc_req->mlo_info.num_partner_links <
767 			    QDF_ARRAY_SIZE(
768 				assoc_req->mlo_info.partner_link_info)) {
769 				qdf_mem_copy(&info, &assoc_req->mlo_info,
770 					     sizeof(info));
771 				linfo =
772 				&info.partner_link_info[info.num_partner_links];
773 				linfo->link_id = wlan_vdev_get_link_id(
774 							pe_session->vdev);
775 				qdf_mem_copy(linfo->link_addr.bytes,
776 					     sta->staAddr, QDF_MAC_ADDR_SIZE);
777 				info.num_partner_links++;
778 				wlan_mlo_peer_create(pe_session->vdev, peer,
779 						     &info,
780 						     assoc_req->assoc_req_buf,
781 						     sta->assocId);
782 			} else {
783 				pe_err("invalid partner link number %d",
784 				       assoc_req->mlo_info.num_partner_links);
785 			}
786 		} else {
787 			ml_peer = wlan_mlo_get_mlpeer_by_aid(
788 					pe_session->vdev->mlo_dev_ctx,
789 					sta->assocId);
790 			if (ml_peer)
791 				wlan_mlo_link_peer_attach(ml_peer, peer, NULL);
792 		}
793 		wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_MAC_ID);
794 	} else {
795 		if (!lim_is_mlo_recv_assoc(sta)) {
796 			ml_peer = wlan_mlo_get_mlpeer_by_aid(
797 					pe_session->vdev->mlo_dev_ctx,
798 					sta->assocId);
799 			if (ml_peer)
800 				wlan_mlo_partner_peer_create_failed_notify(
801 								ml_peer);
802 		}
803 	}
804 }
805 
lim_mlo_partner_auth_type(struct pe_session * session,uint16_t partner_peer_idx,tAniAuthType * auth_type)806 bool lim_mlo_partner_auth_type(struct pe_session *session,
807 			       uint16_t partner_peer_idx,
808 			       tAniAuthType *auth_type)
809 {
810 	bool status = false;
811 	struct pe_session *assoc_link_session = NULL;
812 
813 	tpDphHashNode sta_ds = NULL;
814 
815 	lim_mlo_get_assoc_link_session_sta_ds(session, partner_peer_idx,
816 					      &assoc_link_session, &sta_ds);
817 
818 	if (sta_ds) {
819 		*auth_type = sta_ds->mlmStaContext.authType;
820 		status = true;
821 	} else {
822 		pe_err("sta ds is null");
823 	}
824 
825 	return status;
826 }
827 
lim_mlo_ap_sta_assoc_fail(struct wlan_objmgr_peer * peer)828 void lim_mlo_ap_sta_assoc_fail(struct wlan_objmgr_peer *peer)
829 {
830 	struct mac_context *mac;
831 	struct wlan_objmgr_vdev *vdev;
832 	tpDphHashNode sta;
833 	struct pe_session *pe_session;
834 	uint16_t aid = 0;
835 
836 	mac = cds_get_context(QDF_MODULE_ID_PE);
837 	if (!mac) {
838 		pe_err("mac ctx is null");
839 		return;
840 	}
841 	if (!peer) {
842 		pe_err("peer is null");
843 		return;
844 	}
845 	vdev = wlan_peer_get_vdev(peer);
846 	if (!vdev) {
847 		pe_err("vdev is null");
848 		return;
849 	}
850 	pe_session = pe_find_session_by_vdev_id(
851 			mac, vdev->vdev_objmgr.vdev_id);
852 
853 	if (!pe_session) {
854 		pe_err("pe_session is NULL");
855 		return;
856 	}
857 	sta = dph_lookup_hash_entry(mac, peer->macaddr, &aid,
858 				    &pe_session->dph.dphHashTable);
859 	if (!sta) {
860 		pe_err("sta ds is null");
861 		return;
862 	}
863 	lim_reject_association(mac, sta->staAddr,
864 			       sta->mlmStaContext.subType,
865 			       true, sta->mlmStaContext.authType,
866 			       sta->assocId, true,
867 			       STATUS_UNSPECIFIED_FAILURE,
868 			       pe_session);
869 }
870 
lim_mlo_delete_link_peer(struct pe_session * pe_session,tpDphHashNode sta_ds)871 void lim_mlo_delete_link_peer(struct pe_session *pe_session,
872 			      tpDphHashNode sta_ds)
873 {
874 	struct wlan_objmgr_peer *peer;
875 	struct mac_context *mac;
876 
877 	mac = cds_get_context(QDF_MODULE_ID_PE);
878 	if (!mac) {
879 		pe_err("mac ctx is null");
880 		return;
881 	}
882 	if (!pe_session) {
883 		pe_err("pe session is null");
884 		return;
885 	}
886 	if (!sta_ds) {
887 		pe_err("sta ds is null");
888 		return;
889 	}
890 	if (!lim_is_mlo_conn(pe_session, sta_ds))
891 		return;
892 
893 	peer = wlan_objmgr_get_peer_by_mac(mac->psoc,
894 					   sta_ds->staAddr,
895 					   WLAN_LEGACY_MAC_ID);
896 	if (!peer) {
897 		pe_err("peer is null");
898 		return;
899 	}
900 
901 	wlan_mlo_link_peer_delete(peer);
902 
903 	wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_MAC_ID);
904 }
905 
906 #if defined(SAP_MULTI_LINK_EMULATION)
lim_mlo_assoc_ind_upper_layer(struct mac_context * mac,struct pe_session * pe_session,struct mlo_partner_info * mlo_info)907 QDF_STATUS lim_mlo_assoc_ind_upper_layer(struct mac_context *mac,
908 					 struct pe_session *pe_session,
909 					 struct mlo_partner_info *mlo_info)
910 {
911 	return QDF_STATUS_SUCCESS;
912 }
913 #else
lim_mlo_assoc_ind_upper_layer(struct mac_context * mac,struct pe_session * pe_session,struct mlo_partner_info * mlo_info)914 QDF_STATUS lim_mlo_assoc_ind_upper_layer(struct mac_context *mac,
915 					 struct pe_session *pe_session,
916 					 struct mlo_partner_info *mlo_info)
917 {
918 	int link;
919 	uint8_t link_id;
920 	struct qdf_mac_addr *link_addr;
921 	struct pe_session *lk_session;
922 	tpDphHashNode sta;
923 	uint16_t aid;
924 	struct assoc_ind *sme_assoc_ind;
925 	struct scheduler_msg msg;
926 	tpLimMlmAssocInd lim_assoc_ind;
927 	QDF_STATUS status = QDF_STATUS_E_FAILURE;
928 
929 	if (!mac) {
930 		pe_err("mac is NULL");
931 		return status;
932 	}
933 
934 	if (!pe_session) {
935 		pe_err("pe_session is NULL");
936 		return status;
937 	}
938 
939 	if (!mlo_info) {
940 		pe_err("mlo_info is NULL");
941 		return status;
942 	}
943 
944 	status = QDF_STATUS_SUCCESS;
945 	for (link = 0; link < mlo_info->num_partner_links; link++) {
946 		link_id = mlo_info->partner_link_info[link].link_id;
947 		link_addr = &mlo_info->partner_link_info[link].link_addr;
948 		lk_session = pe_find_partner_session_by_link_id(pe_session,
949 								link_id);
950 		if (!lk_session) {
951 			pe_err("link_session is NULL");
952 			status = QDF_STATUS_E_FAILURE;
953 			break;
954 		}
955 		sta = dph_lookup_hash_entry(mac, link_addr->bytes, &aid,
956 					    &lk_session->dph.dphHashTable);
957 		if (!sta) {
958 			pe_err("sta_ds is NULL");
959 			status = QDF_STATUS_E_FAILURE;
960 			lim_mlo_release_vdev_ref(lk_session->vdev);
961 			break;
962 		}
963 		lim_assoc_ind = qdf_mem_malloc(sizeof(tLimMlmAssocInd));
964 		if (!lim_assoc_ind) {
965 			pe_err("lim assoc ind allocate error");
966 			qdf_mem_free(lk_session->parsedAssocReq[sta->assocId]);
967 			lk_session->parsedAssocReq[sta->assocId] = NULL;
968 			status = QDF_STATUS_E_FAILURE;
969 			lim_mlo_release_vdev_ref(lk_session->vdev);
970 			break;
971 		}
972 
973 		if (!lim_fill_lim_assoc_ind_params(lim_assoc_ind, mac,
974 						   sta, lk_session)) {
975 			pe_err("lim assoc ind fill error");
976 			qdf_mem_free(lim_assoc_ind);
977 			qdf_mem_free(lk_session->parsedAssocReq[sta->assocId]);
978 			lk_session->parsedAssocReq[sta->assocId] = NULL;
979 			status = QDF_STATUS_E_FAILURE;
980 			lim_mlo_release_vdev_ref(lk_session->vdev);
981 			break;
982 		}
983 		sme_assoc_ind = qdf_mem_malloc(sizeof(struct assoc_ind));
984 		if (!sme_assoc_ind) {
985 			pe_err("sme assoc ind allocate error");
986 			qdf_mem_free(lim_assoc_ind);
987 			qdf_mem_free(lk_session->parsedAssocReq[sta->assocId]);
988 			lk_session->parsedAssocReq[sta->assocId] = NULL;
989 			status = QDF_STATUS_E_FAILURE;
990 			lim_mlo_release_vdev_ref(lk_session->vdev);
991 			break;
992 		}
993 
994 		sme_assoc_ind->messageType = eWNI_SME_ASSOC_IND_UPPER_LAYER;
995 		lim_fill_sme_assoc_ind_params(mac, lim_assoc_ind, sme_assoc_ind,
996 					      lk_session, true);
997 
998 		qdf_mem_zero(&msg, sizeof(struct scheduler_msg));
999 		msg.type = eWNI_SME_ASSOC_IND_UPPER_LAYER;
1000 		msg.bodyptr = sme_assoc_ind;
1001 		msg.bodyval = 0;
1002 		sme_assoc_ind->reassocReq = sta->mlmStaContext.subType;
1003 		sme_assoc_ind->timingMeasCap = sta->timingMeasCap;
1004 		MTRACE(mac_trace_msg_tx(mac, lk_session->peSessionId,
1005 					msg.type));
1006 		lim_sys_process_mmh_msg_api(mac, &msg);
1007 
1008 		qdf_mem_free(lim_assoc_ind);
1009 		lim_free_assoc_req_frm_buf(
1010 				lk_session->parsedAssocReq[sta->assocId]);
1011 		qdf_mem_free(lk_session->parsedAssocReq[sta->assocId]);
1012 		lk_session->parsedAssocReq[sta->assocId] = NULL;
1013 		lim_mlo_release_vdev_ref(lk_session->vdev);
1014 	}
1015 
1016 	return status;
1017 }
1018 #endif
1019 
lim_mlo_save_mlo_info(tpDphHashNode sta_ds,struct mlo_partner_info * mlo_info)1020 void lim_mlo_save_mlo_info(tpDphHashNode sta_ds,
1021 			   struct mlo_partner_info *mlo_info)
1022 {
1023 	if (!sta_ds) {
1024 		pe_err("sta ds is null");
1025 		return;
1026 	}
1027 
1028 	qdf_mem_copy(&sta_ds->mlo_info, mlo_info, sizeof(sta_ds->mlo_info));
1029 }
1030 
lim_fill_complete_mlo_ie(struct pe_session * session,uint16_t total_len,uint8_t * target)1031 QDF_STATUS lim_fill_complete_mlo_ie(struct pe_session *session,
1032 				    uint16_t total_len, uint8_t *target)
1033 {
1034 	struct wlan_mlo_sta_profile *sta_prof;
1035 	uint16_t mlo_ie_total_len;
1036 	uint8_t *buf, *pbuf;
1037 	uint16_t i;
1038 	uint16_t consumed = 0;
1039 	uint16_t index = 0;
1040 	struct wlan_mlo_ie *mlo_ie;
1041 
1042 	if (!session)
1043 		return QDF_STATUS_E_INVAL;
1044 
1045 	mlo_ie = &session->mlo_ie;
1046 	if (total_len > WLAN_MAX_IE_LEN + MIN_IE_LEN)
1047 		mlo_ie->data[TAG_LEN_POS] = WLAN_MAX_IE_LEN;
1048 	else
1049 		mlo_ie->data[TAG_LEN_POS] = total_len - MIN_IE_LEN;
1050 
1051 	buf = qdf_mem_malloc(total_len);
1052 	if (!buf)
1053 		return QDF_STATUS_E_NOMEM;
1054 
1055 	pbuf = buf;
1056 	qdf_mem_copy(pbuf, mlo_ie->data, mlo_ie->num_data);
1057 	pbuf += mlo_ie->num_data;
1058 
1059 	for (i = 0; i < mlo_ie->num_sta_profile; i++) {
1060 		sta_prof = &mlo_ie->sta_profile[i];
1061 		qdf_mem_copy(pbuf, sta_prof->data, sta_prof->num_data);
1062 		pbuf += sta_prof->num_data;
1063 	}
1064 
1065 	target[consumed++] = buf[index++];
1066 	target[consumed++] = buf[index++];
1067 	mlo_ie_total_len = pbuf - buf - MIN_IE_LEN;
1068 	if (mlo_ie_total_len > total_len - MIN_IE_LEN) {
1069 		pe_err("Invalid len: %u, %u", mlo_ie_total_len, total_len);
1070 		qdf_mem_free(buf);
1071 		return QDF_STATUS_E_INVAL;
1072 	}
1073 
1074 	for (i = 0; i < mlo_ie_total_len; i++) {
1075 		if (i && (i % WLAN_MAX_IE_LEN) == 0) {
1076 			/* add fragmentation IE and length */
1077 			target[consumed++] = WLAN_ELEMID_FRAGMENT;
1078 			if ((mlo_ie_total_len - i) > WLAN_MAX_IE_LEN)
1079 				target[consumed++] = WLAN_MAX_IE_LEN;
1080 			else
1081 				target[consumed++] = mlo_ie_total_len - i;
1082 		}
1083 		target[consumed++] = buf[index++];
1084 	}
1085 	qdf_mem_free(buf);
1086 	pe_debug("pack mlo ie %d bytes, expected to copy %d bytes",
1087 		 consumed, total_len);
1088 	qdf_trace_hex_dump(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG,
1089 			   target, consumed);
1090 
1091 	return QDF_STATUS_SUCCESS;
1092 }
1093 
lim_caculate_mlo_ie_length(struct wlan_mlo_ie * mlo_ie)1094 uint16_t lim_caculate_mlo_ie_length(struct wlan_mlo_ie *mlo_ie)
1095 {
1096 	struct wlan_mlo_sta_profile *sta_prof;
1097 	uint16_t total_len;
1098 	uint16_t i, tmp;
1099 
1100 	total_len = mlo_ie->num_data;
1101 	for (i = 0; i < mlo_ie->num_sta_profile; i++) {
1102 		sta_prof = &mlo_ie->sta_profile[i];
1103 		total_len += sta_prof->num_data;
1104 	}
1105 
1106 	if (total_len > WLAN_MAX_IE_LEN + MIN_IE_LEN) {
1107 		/* ML IE max length  WLAN_MAX_IE_LEN + MIN_IE_LEN */
1108 		tmp = total_len - (WLAN_MAX_IE_LEN + MIN_IE_LEN);
1109 		while (tmp > WLAN_MAX_IE_LEN) {
1110 			/* add one flagmentation IE */
1111 			total_len += MIN_IE_LEN;
1112 			tmp -= WLAN_MAX_IE_LEN;
1113 		}
1114 		/* add one flagmentation IE */
1115 		total_len += MIN_IE_LEN;
1116 	}
1117 	return total_len;
1118 }
1119 
lim_store_mlo_ie_raw_info(uint8_t * ie,uint8_t * sta_prof_ie,uint32_t total_len,struct wlan_mlo_ie * mlo_ie)1120 QDF_STATUS lim_store_mlo_ie_raw_info(uint8_t *ie, uint8_t *sta_prof_ie,
1121 				     uint32_t total_len,
1122 				     struct wlan_mlo_ie *mlo_ie)
1123 {
1124 	uint32_t i, frag_num = 0, sta_index;
1125 	/* ml_ie_len = total_len - 2 * frag_num, does not include
1126 	 * WLAN_ELEMID_FRAGMENT IE and LEN
1127 	 */
1128 	uint32_t ml_ie_len;
1129 	uint32_t index, copied;
1130 	uint8_t *pfrm;
1131 	uint8_t *buf;
1132 	struct wlan_mlo_sta_profile *sta_prof;
1133 	uint8_t *sta_data;
1134 	/* Per STA profile frag or not */
1135 	bool frag = FALSE;
1136 
1137 	if (!ie)
1138 		return QDF_STATUS_E_INVAL;
1139 
1140 	qdf_mem_zero(mlo_ie, sizeof(*mlo_ie));
1141 
1142 	/* assume element ID + LEN + extension element ID + multi-link control +
1143 	 * common info length always less than WLAN_MAX_IE_LEN
1144 	 */
1145 	mlo_ie->num_data = sta_prof_ie - ie;
1146 	if (mlo_ie->num_data > WLAN_MLO_IE_COM_MAX_LEN) {
1147 		mlo_ie->num_data = 0;
1148 		return QDF_STATUS_E_INVAL;
1149 	}
1150 	qdf_mem_copy(mlo_ie->data, ie, mlo_ie->num_data);
1151 
1152 	/* Count how many frag IE */
1153 	pfrm = ie;
1154 	ml_ie_len = pfrm[TAG_LEN_POS] + MIN_IE_LEN;
1155 	while (ml_ie_len < total_len) {
1156 		frag_num++;
1157 		pfrm += MIN_IE_LEN + pfrm[TAG_LEN_POS];
1158 		ml_ie_len += pfrm[TAG_LEN_POS] + MIN_IE_LEN;
1159 	}
1160 	ml_ie_len = total_len - frag_num * MIN_IE_LEN;
1161 
1162 	pe_debug_rl("ml_ie_len: %d, total_len: %d, frag_num: %d", ml_ie_len,
1163 		    total_len, frag_num);
1164 
1165 	buf = qdf_mem_malloc(total_len);
1166 	if (!buf)
1167 		return QDF_STATUS_E_NOMEM;
1168 
1169 	/* Copy the raw info and skip frag IE */
1170 	index = 0;
1171 	copied = 0;
1172 	buf[index++] = ie[copied++];
1173 	buf[index++] = ie[copied++];
1174 	for (i = 0; i < ml_ie_len - MIN_IE_LEN; i++) {
1175 		/* skip the frag IE */
1176 		if (i && (i % WLAN_MAX_IE_LEN) == 0)
1177 			copied += MIN_IE_LEN;
1178 		buf[index++] = ie[copied++];
1179 	}
1180 
1181 	/* copy sta profile from buf, it has copied the common info */
1182 	sta_index = 0;
1183 	copied = mlo_ie->num_data;
1184 	pfrm = buf + copied;
1185 	while (copied < ml_ie_len && sta_index < WLAN_MLO_MAX_VDEVS &&
1186 	       pfrm[ID_POS] == WLAN_ML_LINFO_SUBELEMID_PERSTAPROFILE) {
1187 		sta_prof = &mlo_ie->sta_profile[sta_index];
1188 		sta_data = sta_prof->data;
1189 		index = 0;
1190 
1191 		sta_data[index++] = buf[copied++];
1192 		sta_data[index++] = buf[copied++];
1193 		do {
1194 			if (index + pfrm[TAG_LEN_POS] >
1195 						WLAN_STA_PROFILE_MAX_LEN) {
1196 				qdf_mem_free(buf);
1197 				pe_debug("no enough buf to store sta prof");
1198 				return QDF_STATUS_E_INVAL;
1199 			}
1200 
1201 			for (i = 0; i < pfrm[TAG_LEN_POS]; i++)
1202 				sta_data[index++] = buf[copied++];
1203 			sta_prof->num_data = index;
1204 
1205 			if (copied < ml_ie_len &&
1206 			    pfrm[TAG_LEN_POS] == WLAN_MAX_IE_LEN &&
1207 			    pfrm[WLAN_MAX_IE_LEN + MIN_IE_LEN] ==
1208 					WLAN_ML_LINFO_SUBELEMID_FRAGMENT) {
1209 				frag = TRUE;
1210 				/* skip sta profile frag IE */
1211 				copied += MIN_IE_LEN;
1212 			} else {
1213 				frag = FALSE;
1214 			}
1215 			pfrm += pfrm[TAG_LEN_POS] + MIN_IE_LEN;
1216 		} while (frag);
1217 		pe_debug_rl("sta index: %d, sta_data len: %d, copied: %d",
1218 			    sta_index, index, copied);
1219 		sta_index++;
1220 	}
1221 
1222 	mlo_ie->num_sta_profile = sta_index;
1223 	qdf_mem_free(buf);
1224 	return QDF_STATUS_SUCCESS;
1225 }
1226 
lim_add_frag_ie_for_sta_profile(uint8_t * data,uint16_t * len)1227 QDF_STATUS lim_add_frag_ie_for_sta_profile(uint8_t *data, uint16_t *len)
1228 {
1229 	uint16_t total_len;
1230 	uint16_t tmp, i;
1231 	uint8_t *buf;
1232 	uint16_t consumed = 0;
1233 	uint16_t index = 0;
1234 
1235 	total_len = *len;
1236 	buf = qdf_mem_malloc(total_len);
1237 	if (!buf)
1238 		return QDF_STATUS_E_NOMEM;
1239 
1240 	qdf_mem_copy(buf, data, total_len);
1241 
1242 	if (total_len > WLAN_MAX_IE_LEN + MIN_IE_LEN) {
1243 		/* ML IE max length  WLAN_MAX_IE_LEN + MIN_IE_LEN */
1244 		tmp = total_len - (WLAN_MAX_IE_LEN + MIN_IE_LEN);
1245 		while (tmp > WLAN_MAX_IE_LEN) {
1246 			/* add one flagmentation IE */
1247 			total_len += MIN_IE_LEN;
1248 			tmp -= WLAN_MAX_IE_LEN;
1249 		}
1250 		/* add one flagmentation IE */
1251 		total_len += MIN_IE_LEN;
1252 	}
1253 
1254 	data[consumed++] = buf[index++];
1255 	data[consumed++] = buf[index++];
1256 	for (i = 0; i < (*len - MIN_IE_LEN); i++) {
1257 		data[consumed++] = buf[index++];
1258 		if (i && (i % WLAN_MAX_IE_LEN) == 0) {
1259 			data[consumed++] = WLAN_ML_LINFO_SUBELEMID_FRAGMENT;
1260 			if ((*len - MIN_IE_LEN - i) > WLAN_MAX_IE_LEN)
1261 				data[consumed++] = WLAN_MAX_IE_LEN;
1262 			else
1263 				data[consumed++] = *len - MIN_IE_LEN - i;
1264 		}
1265 	}
1266 
1267 	*len = total_len;
1268 	qdf_mem_free(buf);
1269 
1270 	return QDF_STATUS_SUCCESS;
1271 }
1272 
1273 uint16_t
lim_fill_assoc_req_mlo_ie(struct mac_context * mac_ctx,struct pe_session * session,tDot11fAssocRequest * frm)1274 lim_fill_assoc_req_mlo_ie(struct mac_context *mac_ctx,
1275 			  struct pe_session *session,
1276 			  tDot11fAssocRequest *frm)
1277 {
1278 	QDF_STATUS status;
1279 
1280 	session->mlo_ie_total_len = 0;
1281 	qdf_mem_zero(&session->mlo_ie, sizeof(session->mlo_ie));
1282 	if ((wlan_vdev_mlme_get_opmode(session->vdev) == QDF_STA_MODE) &&
1283 	    wlan_vdev_mlme_is_mlo_vdev(session->vdev)) {
1284 		status =
1285 			populate_dot11f_assoc_req_mlo_ie(mac_ctx, session, frm);
1286 		if (QDF_IS_STATUS_SUCCESS(status))
1287 			session->mlo_ie_total_len =
1288 				lim_caculate_mlo_ie_length(&session->mlo_ie);
1289 	}
1290 
1291 	return session->mlo_ie_total_len;
1292 }
1293 
1294 uint16_t
lim_send_assoc_rsp_mgmt_frame_mlo(struct mac_context * mac_ctx,struct pe_session * session,tpDphHashNode sta,tDot11fAssocResponse * frm)1295 lim_send_assoc_rsp_mgmt_frame_mlo(struct mac_context *mac_ctx,
1296 				  struct pe_session *session,
1297 				  tpDphHashNode sta,
1298 				  tDot11fAssocResponse *frm)
1299 {
1300 	QDF_STATUS status;
1301 
1302 	session->mlo_ie_total_len = 0;
1303 	qdf_mem_zero(&session->mlo_ie, sizeof(session->mlo_ie));
1304 	status = populate_dot11f_assoc_rsp_mlo_ie(mac_ctx, session, sta, frm);
1305 	if (QDF_IS_STATUS_SUCCESS(status))
1306 		session->mlo_ie_total_len =
1307 				lim_caculate_mlo_ie_length(&session->mlo_ie);
1308 
1309 	return session->mlo_ie_total_len;
1310 }
1311 
1312 uint16_t
lim_send_bcn_frame_mlo(struct mac_context * mac_ctx,struct pe_session * session)1313 lim_send_bcn_frame_mlo(struct mac_context *mac_ctx,
1314 		       struct pe_session *session)
1315 {
1316 	QDF_STATUS status;
1317 
1318 	session->mlo_ie_total_len = 0;
1319 	qdf_mem_zero(&session->mlo_ie, sizeof(session->mlo_ie));
1320 	status = populate_dot11f_bcn_mlo_ie(mac_ctx, session);
1321 	if (QDF_IS_STATUS_SUCCESS(status))
1322 		session->mlo_ie_total_len =
1323 				lim_caculate_mlo_ie_length(&session->mlo_ie);
1324 
1325 	return session->mlo_ie_total_len;
1326 }
1327 
1328 uint16_t
lim_send_probe_req_frame_mlo(struct mac_context * mac_ctx,struct pe_session * session)1329 lim_send_probe_req_frame_mlo(struct mac_context *mac_ctx,
1330 			     struct pe_session *session)
1331 {
1332 	QDF_STATUS status;
1333 
1334 	session->mlo_ie_total_len = 0;
1335 	qdf_mem_zero(&session->mlo_ie, sizeof(session->mlo_ie));
1336 	status = populate_dot11f_probe_req_mlo_ie(mac_ctx, session);
1337 	if (QDF_IS_STATUS_SUCCESS(status))
1338 		session->mlo_ie_total_len =
1339 				lim_caculate_mlo_ie_length(&session->mlo_ie);
1340 
1341 	return session->mlo_ie_total_len;
1342 }
1343 
1344 uint16_t
lim_send_tdls_mgmt_frame_mlo(struct mac_context * mac_ctx,struct pe_session * session)1345 lim_send_tdls_mgmt_frame_mlo(struct mac_context *mac_ctx,
1346 			     struct pe_session *session)
1347 {
1348 	QDF_STATUS status;
1349 
1350 	session->mlo_ie_total_len = 0;
1351 	qdf_mem_zero(&session->mlo_ie, sizeof(session->mlo_ie));
1352 	status = populate_dot11f_tdls_mgmt_mlo_ie(mac_ctx, session);
1353 	if (QDF_IS_STATUS_SUCCESS(status))
1354 		session->mlo_ie_total_len =
1355 				lim_caculate_mlo_ie_length(&session->mlo_ie);
1356 
1357 	return session->mlo_ie_total_len;
1358 }
1359 
1360 uint16_t
lim_get_frame_mlo_ie_len(struct pe_session * session)1361 lim_get_frame_mlo_ie_len(struct pe_session *session)
1362 {
1363 	if (session)
1364 		return session->mlo_ie_total_len;
1365 	else
1366 		return 0;
1367 }
1368 
1369 bool
lim_is_ml_peer_state_disconn(struct mac_context * mac_ctx,struct pe_session * session,uint8_t * mac_addr)1370 lim_is_ml_peer_state_disconn(struct mac_context *mac_ctx,
1371 			     struct pe_session *session,
1372 			     uint8_t *mac_addr)
1373 {
1374 	struct wlan_objmgr_peer *peer;
1375 	struct wlan_mlo_peer_context *ml_peer = NULL;
1376 	bool is_ml_peer_disconn = false;
1377 
1378 	peer = wlan_objmgr_get_peer_by_mac(mac_ctx->psoc, mac_addr,
1379 					   WLAN_LEGACY_MAC_ID);
1380 
1381 	if (!peer) {
1382 		pe_err("peer is NULL");
1383 		return is_ml_peer_disconn;
1384 	}
1385 
1386 	if ((session->opmode == QDF_STA_MODE) &&
1387 	     wlan_vdev_mlme_is_mlo_vdev(session->vdev))
1388 		ml_peer = peer->mlo_peer_ctx;
1389 
1390 	if (!ml_peer) {
1391 		pe_err("ML peer ctx not found");
1392 		goto end;
1393 	}
1394 
1395 	if (QDF_IS_STATUS_SUCCESS(wlan_mlo_peer_is_disconnect_progress(ml_peer)))
1396 		is_ml_peer_disconn = true;
1397 
1398 end:
1399 	wlan_objmgr_peer_release_ref(peer, WLAN_LEGACY_MAC_ID);
1400 	return is_ml_peer_disconn;
1401 }
1402 
lim_is_emlsr_band_supported(struct pe_session * session)1403 bool lim_is_emlsr_band_supported(struct pe_session *session)
1404 {
1405 	uint8_t i;
1406 	uint32_t freq;
1407 	struct mlo_partner_info *partner_info;
1408 
1409 	if (!session->lim_join_req) {
1410 		/* Initial connection */
1411 		partner_info = &session->ml_partner_info;
1412 	} else {
1413 		/* Roaming */
1414 		partner_info = &session->lim_join_req->partner_info;
1415 	}
1416 
1417 	if (wlan_reg_is_24ghz_ch_freq(session->curr_op_freq))
1418 		return false;
1419 
1420 	for (i = 0; i < partner_info->num_partner_links; i++) {
1421 		freq = partner_info->partner_link_info[i].chan_freq;
1422 		if (wlan_reg_is_24ghz_ch_freq(freq))
1423 			return false;
1424 	}
1425 
1426 	return true;
1427 }
1428