xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlo_mgr/src/wlan_mlo_t2lm.c (revision 7d007034fb2934d81e3012120bee9be0e32e9d63)
1 /*
2  * Copyright (c) 2022 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 T2LM APIs
19  */
20 
21 #include <wlan_objmgr_pdev_obj.h>
22 #include <wlan_objmgr_vdev_obj.h>
23 #include <wlan_objmgr_peer_obj.h>
24 #include <wlan_mlo_mgr_public_structs.h>
25 #include <wlan_mlo_t2lm.h>
26 #include <wlan_mlo_mgr_cmn.h>
27 #include <qdf_util.h>
28 
29 /**
30  * wlan_mlo_get_hw_link_id_mask() - Return hw_link_id mask for a given
31  * ieee_link_id_mask.
32  * @wlan_vdev_list: vdev list
33  * @vdev_count: vdev count
34  * @ieee_link_id_mask: IEEE link id mask
35  *
36  * Return: hw_link_id mask
37  */
38 static uint8_t wlan_mlo_get_hw_link_id_mask(
39 		struct wlan_objmgr_vdev **wlan_vdev_list,
40 		uint16_t vdev_count, uint16_t ieee_link_id_mask)
41 {
42 	struct wlan_objmgr_pdev *pdev;
43 	struct wlan_objmgr_vdev *vdev;
44 	uint8_t link_id;
45 	uint16_t ieee_link_id;
46 	uint16_t hw_link_id_mask = 0;
47 	int i;
48 
49 	for (link_id = 0; link_id < WLAN_T2LM_MAX_NUM_LINKS; link_id++) {
50 		ieee_link_id = ieee_link_id_mask & BIT(link_id);
51 		if (!ieee_link_id)
52 			continue;
53 
54 		for (i = 0; i < vdev_count; i++) {
55 			vdev = wlan_vdev_list[i];
56 			if (!vdev) {
57 				t2lm_err("vdev is null");
58 				continue;
59 			}
60 
61 			if (ieee_link_id != BIT(vdev->vdev_mlme.mlo_link_id))
62 				continue;
63 
64 			pdev = wlan_vdev_get_pdev(vdev);
65 			if (!pdev) {
66 				t2lm_err("pdev is null");
67 				continue;
68 			}
69 
70 			hw_link_id_mask |=
71 				BIT(wlan_mlo_get_pdev_hw_link_id(pdev));
72 			break;
73 		}
74 	}
75 
76 	return hw_link_id_mask;
77 }
78 
79 /**
80  * wlan_mlo_parse_t2lm_provisioned_links() - Parse the T2LM provisioned links
81  * from the T2LM IE.
82  * @peer: Pointer to peer structure
83  * @link_mapping_presence_ind: This indicates whether the link mapping of TIDs
84  *                             present in the T2LM IE.
85  * @link_mapping_of_tids: Link mapping of each TID
86  * @t2lm: Pointer to a T2LM structure
87  * @dir: DL/UL/BIDI direction
88  *
89  * Return: QDF_STATUS
90  */
91 static QDF_STATUS wlan_mlo_parse_t2lm_provisioned_links(
92 		struct wlan_objmgr_peer *peer,
93 		uint8_t link_mapping_presence_ind,
94 		uint8_t *link_mapping_of_tids,
95 		struct wlan_t2lm_onging_negotiation_info *t2lm,
96 		enum wlan_t2lm_direction dir)
97 {
98 	struct wlan_objmgr_vdev *vdev;
99 	struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = {0};
100 	uint16_t vdev_count = 0;
101 	uint16_t hw_link_id_mask;
102 	uint8_t tid_num;
103 	uint16_t i;
104 
105 	vdev = wlan_peer_get_vdev(peer);
106 	if (!vdev) {
107 		t2lm_err("vdev is null");
108 		return QDF_STATUS_E_NULL_VALUE;
109 	}
110 
111 	mlo_get_ml_vdev_list(vdev, &vdev_count, wlan_vdev_list);
112 	if (!vdev_count) {
113 		t2lm_err("Number of VDEVs under MLD is reported as 0");
114 		return QDF_STATUS_E_NULL_VALUE;
115 	}
116 
117 	for (tid_num = 0; tid_num < T2LM_MAX_NUM_TIDS; tid_num++) {
118 		if (link_mapping_presence_ind & BIT(tid_num)) {
119 			hw_link_id_mask = wlan_mlo_get_hw_link_id_mask(
120 					wlan_vdev_list, vdev_count,
121 					*(uint16_t *)link_mapping_of_tids);
122 			t2lm->t2lm_info[dir].hw_link_map_tid[tid_num] =
123 				hw_link_id_mask;
124 			link_mapping_of_tids += sizeof(uint16_t);
125 		}
126 	}
127 
128 	for (i = 0; i < vdev_count; i++)
129 		mlo_release_vdev_ref(wlan_vdev_list[i]);
130 
131 	return QDF_STATUS_SUCCESS;
132 }
133 
134 /**
135  * wlan_mlo_parse_t2lm_info() - Parse T2LM IE fields
136  * @peer: Pointer to peer structure
137  * @ie: Pointer to T2LM IE
138  * @t2lm: Pointer to T2LM structure
139  *
140  * Return: QDF_STATUS
141  */
142 static QDF_STATUS wlan_mlo_parse_t2lm_info(
143 		struct wlan_objmgr_peer *peer, uint8_t *ie,
144 		struct wlan_t2lm_onging_negotiation_info *t2lm)
145 {
146 	enum wlan_t2lm_direction dir;
147 	uint8_t *t2lm_control_field;
148 	uint16_t t2lm_control;
149 	uint8_t link_mapping_presence_ind;
150 	uint8_t *link_mapping_of_tids;
151 	uint8_t tid_num;
152 	QDF_STATUS ret;
153 
154 	t2lm_control_field = ie + sizeof(struct wlan_ie_tid_to_link_mapping);
155 	if (!t2lm_control_field) {
156 		t2lm_err("t2lm_control_field is null");
157 		return QDF_STATUS_E_NULL_VALUE;
158 	}
159 
160 	t2lm_control = *(uint16_t *)t2lm_control_field;
161 
162 	dir = QDF_GET_BITS(t2lm_control, WLAN_T2LM_CONTROL_DIRECTION_IDX,
163 			   WLAN_T2LM_CONTROL_DIRECTION_BITS);
164 	if (dir > WLAN_T2LM_BIDI_DIRECTION) {
165 		t2lm_err("Invalid direction");
166 		return QDF_STATUS_E_NULL_VALUE;
167 	}
168 
169 	t2lm->t2lm_info[dir].direction = dir;
170 	t2lm->t2lm_info[dir].default_link_mapping =
171 		QDF_GET_BITS(t2lm_control,
172 			     WLAN_T2LM_CONTROL_DEFAULT_LINK_MAPPING_IDX,
173 			     WLAN_T2LM_CONTROL_DEFAULT_LINK_MAPPING_BITS);
174 
175 	t2lm_debug("direction:%d default_link_mapping:%d",
176 		   t2lm->t2lm_info[dir].direction,
177 		   t2lm->t2lm_info[dir].default_link_mapping);
178 
179 	/* Link mapping of TIDs are not present when default mapping is set */
180 	if (t2lm->t2lm_info[dir].default_link_mapping)
181 		return QDF_STATUS_SUCCESS;
182 
183 	link_mapping_presence_ind = QDF_GET_BITS(
184 		t2lm_control,
185 		WLAN_T2LM_CONTROL_LINK_MAPPING_PRESENCE_INDICATOR_IDX,
186 		WLAN_T2LM_CONTROL_LINK_MAPPING_PRESENCE_INDICATOR_BITS);
187 
188 	link_mapping_of_tids = t2lm_control_field + sizeof(t2lm_control);
189 
190 	ret = wlan_mlo_parse_t2lm_provisioned_links(peer,
191 						    link_mapping_presence_ind,
192 						    link_mapping_of_tids, t2lm,
193 						    dir);
194 	if (ret) {
195 		t2lm_err("failed to populate provisioned links");
196 		return ret;
197 	}
198 
199 	for (tid_num = 0; tid_num < T2LM_MAX_NUM_TIDS; tid_num++) {
200 		if (link_mapping_presence_ind & BIT(tid_num))
201 			t2lm_debug("link mapping of TID%d is %x", tid_num,
202 				   t2lm->t2lm_info[dir].hw_link_map_tid[tid_num]);
203 	}
204 
205 	return ret;
206 }
207 
208 QDF_STATUS wlan_mlo_parse_t2lm_ie(
209 		struct wlan_objmgr_peer *peer,
210 		struct wlan_t2lm_onging_negotiation_info *t2lm, uint8_t *ie)
211 {
212 	struct extn_ie_header *ext_ie_hdr;
213 	enum wlan_t2lm_direction dir;
214 	QDF_STATUS retval;
215 
216 	if (!peer) {
217 		t2lm_err("null peer");
218 		return QDF_STATUS_E_NULL_VALUE;
219 	}
220 
221 	for (dir = 0; dir < WLAN_T2LM_MAX_DIRECTION; dir++)
222 		t2lm->t2lm_info[dir].direction = WLAN_T2LM_INVALID_DIRECTION;
223 
224 	for (dir = 0; dir < WLAN_T2LM_MAX_DIRECTION; dir++) {
225 		if (!ie) {
226 			t2lm_err("ie is null");
227 			return QDF_STATUS_E_NULL_VALUE;
228 		}
229 
230 		ext_ie_hdr = (struct extn_ie_header *)ie;
231 
232 		if (ext_ie_hdr->ie_id == WLAN_ELEMID_EXTN_ELEM &&
233 		    ext_ie_hdr->ie_extn_id == WLAN_EXTN_ELEMID_T2LM) {
234 			retval = wlan_mlo_parse_t2lm_info(peer, ie, t2lm);
235 			if (retval) {
236 				t2lm_err("Failed to parse the T2LM IE");
237 				return retval;
238 			}
239 			ie += ext_ie_hdr->ie_len + sizeof(struct ie_header);
240 		}
241 	}
242 
243 	if ((t2lm->t2lm_info[WLAN_T2LM_DL_DIRECTION].direction ==
244 			WLAN_T2LM_DL_DIRECTION ||
245 	     t2lm->t2lm_info[WLAN_T2LM_UL_DIRECTION].direction ==
246 			WLAN_T2LM_UL_DIRECTION) &&
247 	    t2lm->t2lm_info[WLAN_T2LM_BIDI_DIRECTION].direction ==
248 			WLAN_T2LM_BIDI_DIRECTION) {
249 		t2lm_err("Both DL/UL and BIDI T2LM IEs should not be present at the same time");
250 
251 		qdf_mem_zero(t2lm, sizeof(*t2lm));
252 		for (dir = 0; dir < WLAN_T2LM_MAX_DIRECTION; dir++) {
253 			t2lm->t2lm_info[dir].direction =
254 			    WLAN_T2LM_INVALID_DIRECTION;
255 		}
256 
257 		return QDF_STATUS_E_FAILURE;
258 	}
259 
260 	return QDF_STATUS_SUCCESS;
261 }
262 
263 /**
264  * wlan_get_ieee_link_id_mask() - Get ieee_link_id mask from hw_link_id mask
265  * @wlan_vdev_list: Pointer to vdev list
266  * @vdev_count: vdev count
267  * @hw_link_id_mask: hw_link_id mask
268  *
269  * Return: IEEE link ID mask
270  */
271 static uint8_t wlan_get_ieee_link_id_mask(
272 		struct wlan_objmgr_vdev **wlan_vdev_list,
273 		uint16_t vdev_count, uint16_t hw_link_id_mask)
274 {
275 	struct wlan_objmgr_pdev *pdev;
276 	struct wlan_objmgr_vdev *vdev;
277 	uint8_t link_id;
278 	uint16_t hw_link_id;
279 	uint16_t ieee_link_id_mask = 0;
280 	int i;
281 
282 	for (link_id = 0; link_id < WLAN_T2LM_MAX_NUM_LINKS; link_id++) {
283 		hw_link_id = hw_link_id_mask & BIT(link_id);
284 		if (!hw_link_id)
285 			continue;
286 
287 		for (i = 0; i < vdev_count; i++) {
288 			vdev = wlan_vdev_list[i];
289 			if (!vdev) {
290 				t2lm_err("vdev is null");
291 				continue;
292 			}
293 
294 			pdev = wlan_vdev_get_pdev(vdev);
295 			if (!pdev) {
296 				t2lm_err("pdev is null");
297 				continue;
298 			}
299 
300 			if (hw_link_id != BIT(wlan_mlo_get_pdev_hw_link_id(pdev)))
301 				continue;
302 
303 			ieee_link_id_mask |= BIT(vdev->vdev_mlme.mlo_link_id);
304 			break;
305 		}
306 	}
307 
308 	return ieee_link_id_mask;
309 }
310 
311 /**
312  * wlan_mlo_add_t2lm_provisioned_links() - API to add provisioned links in the
313  * T2LM IE
314  * @peer: Pointer to peer structure
315  * @link_mapping_presence_indicator: This indicates whether the link mapping of
316  *                                   TIDs present in the T2LM IE.
317  * @link_mapping_of_tids: Link mapping of each TID
318  * @t2lm: Pointer to a T2LM structure
319  * @num_tids: Pointer to save num TIDs
320  *
321  * Return: QDF_STATUS
322  */
323 static QDF_STATUS wlan_mlo_add_t2lm_provisioned_links(
324 		struct wlan_objmgr_peer *peer,
325 		uint8_t link_mapping_presence_indicator,
326 		uint8_t *link_mapping_of_tids, struct wlan_t2lm_info *t2lm,
327 		uint8_t *num_tids)
328 {
329 	struct wlan_objmgr_vdev *vdev;
330 	struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = {0};
331 	uint16_t vdev_count = 0;
332 	uint16_t hw_link_id_mask;
333 	uint16_t ieee_link_id_mask;
334 	uint8_t tid_num;
335 	uint16_t i;
336 
337 	vdev = wlan_peer_get_vdev(peer);
338 	if (!vdev) {
339 		t2lm_err("vdev is null");
340 		return QDF_STATUS_E_NULL_VALUE;
341 	}
342 
343 	mlo_get_ml_vdev_list(vdev, &vdev_count, wlan_vdev_list);
344 	if (!vdev_count) {
345 		t2lm_err("Number of VDEVs under MLD is reported as 0");
346 		return QDF_STATUS_E_NULL_VALUE;
347 	}
348 
349 	for (tid_num = 0; tid_num < T2LM_MAX_NUM_TIDS; tid_num++) {
350 		ieee_link_id_mask = 0;
351 
352 		if (t2lm->hw_link_map_tid[tid_num]) {
353 			hw_link_id_mask = t2lm->hw_link_map_tid[tid_num];
354 			ieee_link_id_mask = wlan_get_ieee_link_id_mask(
355 					wlan_vdev_list, vdev_count,
356 					hw_link_id_mask);
357 		}
358 
359 		*(uint16_t *)link_mapping_of_tids = ieee_link_id_mask;
360 		t2lm_info("link mapping of TID%d is %x", tid_num,
361 			  ieee_link_id_mask);
362 		link_mapping_of_tids += sizeof(uint16_t);
363 		(*num_tids)++;
364 	}
365 
366 	for (i = 0; i < vdev_count; i++)
367 		mlo_release_vdev_ref(wlan_vdev_list[i]);
368 
369 	return QDF_STATUS_SUCCESS;
370 }
371 
372 /**
373  * wlan_add_t2lm_info_ie() - Add T2LM IE for UL/DL/Bidirection
374  * @frm: Pointer to buffer
375  * @peer: Pointer to peer structure
376  * @t2lm: Pointer to t2lm mapping structure
377  *
378  * Return: Updated frame pointer
379  */
380 static uint8_t *wlan_add_t2lm_info_ie(uint8_t *frm,
381 				      struct wlan_objmgr_peer *peer,
382 				      struct wlan_t2lm_info *t2lm)
383 {
384 	struct wlan_ie_tid_to_link_mapping *t2lm_ie;
385 	uint16_t t2lm_control = 0;
386 	uint8_t *t2lm_control_field;
387 	uint8_t *link_mapping_of_tids;
388 	uint8_t tid_num;
389 	uint8_t num_tids = 0;
390 	uint8_t link_mapping_presence_indicator = 0;
391 	uint8_t *efrm = frm;
392 	int ret;
393 
394 	t2lm_ie = (struct wlan_ie_tid_to_link_mapping *)frm;
395 	t2lm_ie->elem_id = WLAN_ELEMID_EXTN_ELEM;
396 	t2lm_ie->elem_id_extn = WLAN_EXTN_ELEMID_T2LM;
397 
398 	t2lm_ie->elem_len = sizeof(*t2lm_ie) - sizeof(struct ie_header);
399 
400 	t2lm_control_field = t2lm_ie->data;
401 
402 	QDF_SET_BITS(t2lm_control, WLAN_T2LM_CONTROL_DIRECTION_IDX,
403 		     WLAN_T2LM_CONTROL_DIRECTION_BITS, t2lm->direction);
404 
405 	QDF_SET_BITS(t2lm_control, WLAN_T2LM_CONTROL_DEFAULT_LINK_MAPPING_IDX,
406 		     WLAN_T2LM_CONTROL_DEFAULT_LINK_MAPPING_BITS,
407 		     t2lm->default_link_mapping);
408 
409 	if (t2lm->default_link_mapping) {
410 		/* Link mapping of TIDs are not present when default mapping is
411 		 * set. Hence, the size of TID-To-Link mapping control is one
412 		 * octet.
413 		 */
414 		*t2lm_control_field = (uint8_t)t2lm_control;
415 
416 		t2lm_ie->elem_len += sizeof(uint8_t);
417 
418 		t2lm_info("T2LM IE added, default_link_mapping: %d dir:%d",
419 			  t2lm->default_link_mapping, t2lm->direction);
420 
421 		frm += sizeof(*t2lm_ie) + sizeof(uint8_t);
422 	} else {
423 		for (tid_num = 0; tid_num < T2LM_MAX_NUM_TIDS; tid_num++)
424 			if (t2lm->hw_link_map_tid[tid_num])
425 				link_mapping_presence_indicator |= BIT(tid_num);
426 
427 		QDF_SET_BITS(t2lm_control,
428 			     WLAN_T2LM_CONTROL_LINK_MAPPING_PRESENCE_INDICATOR_IDX,
429 			     WLAN_T2LM_CONTROL_LINK_MAPPING_PRESENCE_INDICATOR_BITS,
430 			     link_mapping_presence_indicator);
431 		t2lm_info("T2LM IE added, direction:%d link_mapping_presence_indicator:%x",
432 			  t2lm->direction, link_mapping_presence_indicator);
433 
434 		/* The size of TID-To-Link mapping control is two octets when
435 		 * default link mapping is not set.
436 		 */
437 		*(uint16_t *)t2lm_control_field = htole16(t2lm_control);
438 
439 		link_mapping_of_tids = t2lm_control_field + sizeof(uint16_t);
440 
441 		ret = wlan_mlo_add_t2lm_provisioned_links(
442 			peer, link_mapping_presence_indicator,
443 			link_mapping_of_tids, t2lm, &num_tids);
444 		if (ret) {
445 			t2lm_err("Failed to add t2lm provisioned links");
446 			return efrm;
447 		}
448 
449 		t2lm_ie->elem_len += sizeof(uint16_t) +
450 				     (num_tids * sizeof(uint16_t));
451 
452 		frm += sizeof(*t2lm_ie) + sizeof(uint16_t) +
453 		       (num_tids * sizeof(uint16_t));
454 	}
455 
456 	return frm;
457 }
458 
459 uint8_t *wlan_mlo_add_t2lm_ie(uint8_t *frm, struct wlan_objmgr_peer *peer,
460 			      struct wlan_t2lm_onging_negotiation_info *t2lm)
461 {
462 	uint8_t dir;
463 
464 	if (!frm) {
465 		t2lm_err("frm is null");
466 		return NULL;
467 	}
468 
469 	if (!t2lm) {
470 		t2lm_err("t2lm is null");
471 		return NULL;
472 	}
473 
474 	/* As per spec, the frame should include one or two T2LM IEs. When it is
475 	 * two, then direction should DL and UL.
476 	 */
477 	if ((t2lm->t2lm_info[WLAN_T2LM_DL_DIRECTION].direction ==
478 			WLAN_T2LM_DL_DIRECTION ||
479 	     t2lm->t2lm_info[WLAN_T2LM_UL_DIRECTION].direction ==
480 			WLAN_T2LM_UL_DIRECTION) &&
481 	    t2lm->t2lm_info[WLAN_T2LM_BIDI_DIRECTION].direction ==
482 			WLAN_T2LM_BIDI_DIRECTION) {
483 		t2lm_err("Both DL/UL and BIDI T2LM IEs should not be present at the same time");
484 		return NULL;
485 	}
486 
487 	for (dir = 0; dir < WLAN_T2LM_MAX_DIRECTION; dir++) {
488 		if (t2lm->t2lm_info[dir].direction !=
489 			WLAN_T2LM_INVALID_DIRECTION)
490 			frm = wlan_add_t2lm_info_ie(frm, peer,
491 						    &t2lm->t2lm_info[dir]);
492 	}
493 
494 	return frm;
495 }
496 
497 /**
498  * wlan_mlo_parse_t2lm_request_action_frame() - API to parse T2LM request action
499  * frame.
500  * @peer: Pointer to peer structure
501  * @t2lm: Pointer to T2LM structure
502  * @action_frm: Pointer to action frame
503  * @category: T2LM action frame category
504  *
505  * Return: QDF_STATUS
506  */
507 static QDF_STATUS wlan_mlo_parse_t2lm_request_action_frame(
508 		struct wlan_objmgr_peer *peer,
509 		struct wlan_t2lm_onging_negotiation_info *t2lm,
510 		struct wlan_action_frame *action_frm,
511 		enum wlan_t2lm_category category)
512 {
513 	uint8_t *t2lm_action_frm;
514 
515 	t2lm->category = category;
516 
517 	/*
518 	 * T2LM request action frame
519 	 *
520 	 *   1-byte     1-byte     1-byte   variable
521 	 *-------------------------------------------
522 	 * |         |           |        |         |
523 	 * | Category| Protected | Dialog | T2LM IE |
524 	 * |         |    EHT    | token  |         |
525 	 * |         |  Action   |        |         |
526 	 *-------------------------------------------
527 	 */
528 
529 	t2lm_action_frm = (uint8_t *)action_frm + sizeof(*action_frm);
530 
531 	t2lm->dialog_token = *t2lm_action_frm;
532 
533 	return wlan_mlo_parse_t2lm_ie(peer, t2lm,
534 				      t2lm_action_frm + sizeof(uint8_t));
535 }
536 
537 /**
538  * wlan_mlo_parse_t2lm_response_action_frame() - API to parse T2LM response
539  * action frame.
540  * @peer: Pointer to peer structure
541  * @t2lm: Pointer to T2LM structure
542  * @action_frm: Pointer to action frame
543  * @category: T2LM action frame category
544  *
545  * Return: QDF_STATUS
546  */
547 static QDF_STATUS wlan_mlo_parse_t2lm_response_action_frame(
548 		struct wlan_objmgr_peer *peer,
549 		struct wlan_t2lm_onging_negotiation_info *t2lm,
550 		struct wlan_action_frame *action_frm,
551 		enum wlan_t2lm_category category)
552 {
553 	uint8_t *t2lm_action_frm;
554 	QDF_STATUS ret_val = QDF_STATUS_SUCCESS;
555 
556 	t2lm->category = WLAN_T2LM_CATEGORY_RESPONSE;
557 	/*
558 	 * T2LM response action frame
559 	 *
560 	 *   1-byte     1-byte     1-byte   1-byte   variable
561 	 *----------------------------------------------------
562 	 * |         |           |        |        |         |
563 	 * | Category| Protected | Dialog | Status | T2LM IE |
564 	 * |         |    EHT    | token  |  code  |         |
565 	 * |         |  Action   |        |        |         |
566 	 *----------------------------------------------------
567 	 */
568 
569 	t2lm_action_frm = (uint8_t *)action_frm + sizeof(*action_frm);
570 
571 	t2lm->dialog_token = *t2lm_action_frm;
572 	t2lm->t2lm_resp_type = *(t2lm_action_frm + sizeof(uint8_t));
573 
574 	if (t2lm->t2lm_resp_type ==
575 			WLAN_T2LM_RESP_TYPE_PREFERRED_TID_TO_LINK_MAPPING) {
576 		t2lm_action_frm += sizeof(uint8_t) + sizeof(uint8_t);
577 		ret_val = wlan_mlo_parse_t2lm_ie(peer, t2lm, t2lm_action_frm);
578 	}
579 
580 	return ret_val;
581 }
582 
583 int wlan_mlo_parse_t2lm_action_frame(
584 		struct wlan_objmgr_peer *peer,
585 		struct wlan_t2lm_onging_negotiation_info *t2lm,
586 		struct wlan_action_frame *action_frm,
587 		enum wlan_t2lm_category category)
588 {
589 	QDF_STATUS ret_val = QDF_STATUS_SUCCESS;
590 
591 	switch (category) {
592 	case WLAN_T2LM_CATEGORY_REQUEST:
593 		{
594 			ret_val = wlan_mlo_parse_t2lm_request_action_frame(
595 					peer, t2lm, action_frm, category);
596 			return qdf_status_to_os_return(ret_val);
597 		}
598 	case WLAN_T2LM_CATEGORY_RESPONSE:
599 		{
600 			ret_val = wlan_mlo_parse_t2lm_response_action_frame(
601 					peer, t2lm, action_frm, category);
602 
603 			return qdf_status_to_os_return(ret_val);
604 		}
605 	case WLAN_T2LM_CATEGORY_TEARDOWN:
606 			/* Nothing to parse from T2LM teardown frame, just reset
607 			 * the mapping to default mapping.
608 			 *
609 			 * T2LM teardown action frame
610 			 *
611 			 *   1-byte     1-byte
612 			 *------------------------
613 			 * |         |           |
614 			 * | Category| Protected |
615 			 * |         |    EHT    |
616 			 * |         |  Action   |
617 			 *------------------------
618 			 */
619 			break;
620 	default:
621 			t2lm_err("Invalid category:%d", category);
622 	}
623 
624 	return ret_val;
625 }
626 
627 static uint8_t *wlan_mlo_add_t2lm_request_action_frame(
628 		struct wlan_objmgr_peer *peer, uint8_t *frm,
629 		struct wlan_action_frame_args *args, uint8_t *buf,
630 		enum wlan_t2lm_category category)
631 {
632 	*frm++ = args->category;
633 	*frm++ = args->action;
634 	/* Dialog token*/
635 	*frm++ = args->arg1;
636 
637 	t2lm_info("T2LM request frame: category:%d action:%d dialog_token:%d",
638 		  args->category, args->action, args->arg1);
639 	return wlan_mlo_add_t2lm_ie(frm, peer, (void *)buf);
640 }
641 
642 static uint8_t *wlan_mlo_add_t2lm_response_action_frame(
643 		struct wlan_objmgr_peer *peer, uint8_t *frm,
644 		struct wlan_action_frame_args *args, uint8_t *buf,
645 		enum wlan_t2lm_category category)
646 {
647 	*frm++ = args->category;
648 	*frm++ = args->action;
649 	/* Dialog token*/
650 	*frm++ = args->arg1;
651 	/* Status code */
652 	*frm++ = args->arg2;
653 
654 	t2lm_info("T2LM response frame: category:%d action:%d dialog_token:%d status_code:%d",
655 		  args->category, args->action, args->arg1, args->arg2);
656 
657 	if (args->arg2 == WLAN_T2LM_RESP_TYPE_PREFERRED_TID_TO_LINK_MAPPING)
658 		frm = wlan_mlo_add_t2lm_ie(frm, peer, (void *)buf);
659 
660 	return frm;
661 }
662 
663 uint8_t *wlan_mlo_add_t2lm_action_frame(
664 		struct wlan_objmgr_peer *peer, uint8_t *frm,
665 		struct wlan_action_frame_args *args, uint8_t *buf,
666 		enum wlan_t2lm_category category)
667 {
668 	if (!peer) {
669 		t2lm_err("null peer");
670 		return NULL;
671 	}
672 
673 	switch (category) {
674 	case WLAN_T2LM_CATEGORY_REQUEST:
675 		return wlan_mlo_add_t2lm_request_action_frame(peer, frm, args,
676 							      buf, category);
677 	case WLAN_T2LM_CATEGORY_RESPONSE:
678 		return wlan_mlo_add_t2lm_response_action_frame(peer, frm, args,
679 							      buf, category);
680 	case WLAN_T2LM_CATEGORY_TEARDOWN:
681 		*frm++ = args->category;
682 		*frm++ = args->action;
683 		return frm;
684 	default:
685 		t2lm_err("Invalid category:%d", category);
686 	}
687 
688 	return frm;
689 }
690