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