Files
linux-nv-oot/drivers/net/wireless/realtek/rtl8852ce/phl/phl_ie.c
Shobek Attupurath 7dd632ff96 rtl8852ce: Add base driver v1.19.16.1-0-g1fe335ba1.20240815_PC
- support Android-14
- support Linux kernel 6.9
- support 6G regulation
- support Thermal protection
- support TX shortcut to reduce CPU loading
- fix some coverity issues
- Use RTW regulatory version rtk_8852CE_M.2_2230-67-52
- default enable con-current and MCC

Bug 4667769
Bug 4667981

Change-Id: Iee069ecdd1f00a0b78285d0a4ef5778ed9ace478
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3195601
Tested-by: Shobek Attupurath <sattupurath@nvidia.com>
Reviewed-by: Revanth Kumar Uppala <ruppala@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>
2025-07-24 10:19:08 +00:00

1358 lines
39 KiB
C

/******************************************************************************
*
* Copyright(c) 2019 - 2021 Realtek Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*****************************************************************************/
#define _PHL_IE_C_
#include "phl_headers.h"
u8 _phl_build_ml_common_info(struct rtw_phl_com_t *phl_com,
struct rtw_phl_ml_ie_info *info,
u8 *pbuf,
u8 *ml_ctrl)
{
struct rtw_phl_stainfo_t *sta = NULL;
void *phl = phl_com->phl_priv;
void *drv = phl_com->drv_priv;
u8 len = 0;
u8 *pstart = pbuf;
u8 *p = pbuf + 1; /* skip Common Info Length subfield */
if (info->rlink == NULL) {
PHL_WARN("%s: wrong rlink assignment!\n", __func__);
goto _exit;
}
sta = rtw_phl_get_stainfo_self(phl, info->rlink);
/* subfields */
switch (GET_ML_ELE_ML_CTRL_TYPE(ml_ctrl)) {
case BASIC_ML:
/* MLD Mac Address subfield */
_os_mem_cpy(drv, p, info->rlink->wrole->mac_addr, ETH_ALEN);
p += ETH_ALEN;
if (GET_ML_ELE_ML_CTRL_LINK_ID_INFO_PRESENT(ml_ctrl)) {
SET_ML_ELE_COMMON_INFO_LINK_ID(p, sta->link_id);
p += 1;
}
if (GET_ML_ELE_ML_CTRL_BSS_PARAMS_CHG_CNT_PRESENT(ml_ctrl)) {
u8 val = info->rlink->bss_params_chg_cnt;
SET_ML_ELE_COMMON_INFO_BSS_PARAMS_CHG_CNT(p, val);
p += 1;
}
if (GET_ML_ELE_ML_CTRL_MEDIUM_SYNC_DELAY_INFO_PRESENT(ml_ctrl)) {
p += 2;
}
if (GET_ML_ELE_ML_CTRL_EML_CAP_PRESENT(ml_ctrl)) {
p += 3;
}
if (GET_ML_ELE_ML_CTRL_MLD_CAP_PRESENT(ml_ctrl)) {
p += 2;
}
break;
case PROBE_REQUEST_ML:
if (GET_ML_ELE_ML_CTRL_MLD_ID_PRESENT(ml_ctrl)) {
SET_ML_ELE_COMMON_INFO_MLD_ID(p, info->mld_id);
p += 1;
}
break;
default:
break;
}
_exit:
len = (u8)(p - pstart);
*pstart = len; /* Common Info Length */
return len;
}
u8 _phl_build_basic_ml_ie(struct rtw_phl_com_t *phl_com,
struct rtw_phl_ml_ie_info *info,
u8 *pbuf,
u8 *ml_ctrl)
{
void *drv = phl_com->drv_priv;
u8 len = 0;
u8 *pstart = pbuf;
u8 *p = pbuf;
/* Element ID Extension field */
*p++ = EID_EXT_MULTI_LINK;
/* ML Control field */
_os_mem_cpy(drv, p, ml_ctrl, 2);
p += 2;
/* ML Common Info field */
p += _phl_build_ml_common_info(phl_com, info, p, ml_ctrl);
/* optional subelements */
if (info->opt_len) {
_os_mem_cpy(drv, p, info->opt, info->opt_len);
p += info->opt_len;
}
len = (u8)(p - pstart);
return len;
}
u8 _phl_build_probe_request_ml_ie(struct rtw_phl_com_t *phl_com,
struct rtw_phl_ml_ie_info *info,
u8 *pbuf,
u8 *ml_ctrl)
{
void *drv = phl_com->drv_priv;
u8 len = 0;
u8 *pstart = pbuf;
u8 *p = pbuf;
/* Element ID Extension field */
*p++ = EID_EXT_MULTI_LINK;
/* ML Control field */
_os_mem_cpy(drv, p, ml_ctrl, 2);
p += 2;
/* ML Common Info field */
p += _phl_build_ml_common_info(phl_com, info, p, ml_ctrl);
/* optional subelements */
if (info->opt_len) {
_os_mem_cpy(drv, p, info->opt, info->opt_len);
p += info->opt_len;
}
len = (u8)(p - pstart);
return len;
}
u8 rtw_phl_build_ml_ie(struct rtw_phl_com_t *phl_com,
struct rtw_phl_ml_ie_info *info,
u8 *pbuf)
{
u8 len = 0;
u8 *pstart = pbuf;
u8 *p = pbuf + 2;
u8 ml_ctrl[2] = {0};
/* set ML Control field in advance */
switch (info->pkt_type) {
case PACKET_BEACON:
if (info->critical_update)
info->rlink->bss_params_chg_cnt++;
fallthrough;
case PACKET_PROBE_RESPONSE:
case PACKET_PROBE_RESPONSE_ML:
SET_ML_ELE_ML_CTRL_TYPE(ml_ctrl, BASIC_ML);
SET_ML_ELE_ML_CTRL_LINK_ID_INFO_PRESENT(ml_ctrl, true);
SET_ML_ELE_ML_CTRL_BSS_PARAMS_CHG_CNT_PRESENT(ml_ctrl, true);
len = _phl_build_basic_ml_ie(phl_com, info, p, ml_ctrl);
break;
case PACKET_PROBE_REQUEST_ML:
SET_ML_ELE_ML_CTRL_TYPE(ml_ctrl, PROBE_REQUEST_ML);
if (info->mld_id_present)
SET_ML_ELE_ML_CTRL_MLD_ID_PRESENT(ml_ctrl, true);
len = _phl_build_probe_request_ml_ie(phl_com,
info,
p,
ml_ctrl);
break;
case PACKET_AUTH:
SET_ML_ELE_ML_CTRL_TYPE(ml_ctrl, BASIC_ML);
/* LINK_ID_INFO, BSS_PARAMS_CHG_CNT, MEDIUM_SYNC_DELAY_INFO are false */
len = _phl_build_basic_ml_ie(phl_com, info, p, ml_ctrl);
break;
case PACKET_ASSOC_REQUEST:
SET_ML_ELE_ML_CTRL_TYPE(ml_ctrl, BASIC_ML);
/* LINK_ID_INFO, BSS_PARAMS_CHG_CNT, MEDIUM_SYNC_DELAY_INFO are false */
SET_ML_ELE_ML_CTRL_EML_CAP_PRESENT(ml_ctrl, true);
SET_ML_ELE_ML_CTRL_MLD_CAP_PRESENT(ml_ctrl, true);
len = _phl_build_basic_ml_ie(phl_com, info, p, ml_ctrl);
break;
case PACKET_ASSOC_RESPONSE:
SET_ML_ELE_ML_CTRL_TYPE(ml_ctrl, BASIC_ML);
SET_ML_ELE_ML_CTRL_LINK_ID_INFO_PRESENT(ml_ctrl, true);
SET_ML_ELE_ML_CTRL_BSS_PARAMS_CHG_CNT_PRESENT(ml_ctrl, true);
SET_ML_ELE_ML_CTRL_EML_CAP_PRESENT(ml_ctrl, true);
SET_ML_ELE_ML_CTRL_MLD_CAP_PRESENT(ml_ctrl, true);
len = _phl_build_basic_ml_ie(phl_com, info, p, ml_ctrl);
break;
default:
break;
}
*pstart = EID_EXTENSION;
*(pstart + 1) = len;
len += 2;
return len;
}
u8 _phl_build_sta_info(struct rtw_phl_com_t *phl_com,
struct rtw_phl_per_sta_profile_info *info,
u8 *pbuf,
u8 *sta_ctrl)
{
void *drv = phl_com->drv_priv;
u8 len = 0;
u8 *pstart = pbuf;
u8 *p = pbuf + 1; /* skip STA Info Length subfield */
/* subfields */
if (GET_STA_CTRL_MAC_ADDR_PRESENT(sta_ctrl)) {
_os_mem_cpy(drv, p, info->rlink->mac_addr, ETH_ALEN);
p += ETH_ALEN;
}
if (GET_STA_CTRL_BEACON_INTEREVAL_PRESENT(sta_ctrl)) {
#ifdef RTW_PHL_BCN
u16 bcn_interval = (u16)info->rlink->bcn_cmn.bcn_interval;
#else
u16 bcn_interval = 100;
#endif
_os_mem_cpy(drv, p, &(bcn_interval), 2);
p += 2;
}
if (GET_STA_CTRL_DTIM_INFO_PRESENT(sta_ctrl)) {
u16 dtim_info = 0;
u8 dtim_cnt = 0;
u8 dtim_period = 0;
/* DTIM cnt should be refine later if DTIM period is not 1 */
dtim_period = (u8)info->rlink->dtim_period;
dtim_info = (dtim_period << 8) | dtim_cnt;
_os_mem_cpy(drv, p, &(dtim_info), 2);
p += 2;
}
if (GET_STA_CTRL_NSTR_LINK_PAIR_PRESENT(sta_ctrl)) {
if (GET_STA_CTRL_NSTR_BITMAP_SIZE(sta_ctrl) == 0) {
/* TODO */
p += 1;
}
else if (GET_STA_CTRL_NSTR_BITMAP_SIZE(sta_ctrl) == 1) {
/* TODO */
p += 2;
}
}
len = (u8)(p - pstart);
*pstart = len; /* STA Info Length */
return len;
}
u8 rtw_phl_build_per_sta_profile(struct rtw_phl_com_t *phl_com,
struct rtw_phl_per_sta_profile_info *info,
u8 *pbuf)
{
struct rtw_phl_stainfo_t *sta = NULL;
void *phl = phl_com->phl_priv;
void *drv = phl_com->drv_priv;
u8 len = 0, total_len = 0;
u8 *pstart = pbuf;
u8 *p = pbuf + 2;
u8 sta_ctrl[2] = {0};
if (info->rlink == NULL) {
PHL_WARN("%s: wrong rlink assignment!\n", __func__);
return total_len;
}
sta = rtw_phl_get_stainfo_self(phl, info->rlink);
/* STA Control field */
switch (info->pkt_type) {
case PACKET_BEACON:
case PACKET_PROBE_RESPONSE:
SET_STA_CTRL_LINK_ID(sta_ctrl, sta->link_id);
break;
case PACKET_PROBE_RESPONSE_ML:
SET_STA_CTRL_LINK_ID(sta_ctrl, sta->link_id);
if (info->complete_profile) {
SET_STA_CTRL_COMPLETE_PROFILE(sta_ctrl, true);
SET_STA_CTRL_MAC_ADDR_PRESENT(sta_ctrl, true);
SET_STA_CTRL_BEACON_INTEREVAL_PRESENT(sta_ctrl, true);
SET_STA_CTRL_DTIM_INFO_PRESENT(sta_ctrl, true);
}
break;
case PACKET_ASSOC_RESPONSE:
SET_STA_CTRL_LINK_ID(sta_ctrl, sta->link_id);
SET_STA_CTRL_COMPLETE_PROFILE(sta_ctrl, true);
SET_STA_CTRL_MAC_ADDR_PRESENT(sta_ctrl, true);
SET_STA_CTRL_BEACON_INTEREVAL_PRESENT(sta_ctrl, true);
SET_STA_CTRL_DTIM_INFO_PRESENT(sta_ctrl, true);
break;
case PACKET_PROBE_REQUEST_ML:
SET_STA_CTRL_LINK_ID(sta_ctrl, info->link_id);
if (info->complete_profile) {
SET_STA_CTRL_COMPLETE_PROFILE(sta_ctrl, true);
}
break;
case PACKET_ASSOC_REQUEST:
SET_STA_CTRL_LINK_ID(sta_ctrl, info->link_id);
SET_STA_CTRL_COMPLETE_PROFILE(sta_ctrl, true);
SET_STA_CTRL_MAC_ADDR_PRESENT(sta_ctrl, true);
break;
default:
break;
}
/* STA Control field */
_os_mem_cpy(drv, p, sta_ctrl, 2);
p += 2;
total_len += 2;
if (info->pkt_type != PACKET_PROBE_REQUEST_ML) {
/* STA Info field */
len = _phl_build_sta_info(phl_com, info, p, sta_ctrl);
p += len;
total_len += len;
}
/* STA Profile field */
if (info->sta_profile_len) {
_os_mem_cpy(drv, p, info->sta_profile, info->sta_profile_len);
total_len += info->sta_profile_len;
}
*pstart = WLAN_SUBEID_PER_STA_PROFILE;
*(pstart + 1) = total_len;
total_len += 2;
return total_len;
}
void _set_link_mapping(u8 *ele_pos,
u16 *tid2link,
u8 *indicator,
u8 *total_len)
{
u8 idx = 0;
u8 *ele_linkmap = ele_pos;
*indicator = 0;
for(idx = 0; idx < WMM_AC_TID_NUM; idx++) {
if(tid2link[idx]) {
SET_LINK_MAPPING_TID(ele_linkmap, tid2link[idx]);
ele_linkmap +=2;
*total_len += 2;
*indicator |= BIT(idx);
}
}
}
/*
* (Re)Association Request/Response
* Tid-To-Link Mapping Request/Response action frame
*/
u8 rtw_phl_build_tid2link(struct rtw_wifi_role_link_t *rlink,
u8 *ele_start)
{
struct protocol_cap_t *protocol_cap = &rlink->protocol_cap;
u8 *ele_pos = NULL, *ele_linkmap = 0;
u8 def_mapping = true, indicator = 0, idx = 0;
u8 total_len = 0;
/* Element ID */
ele_start[0] = EID_EXTENSION;
/* Element ID Extension field */
ele_start[2] = EID_EXT_TID_TO_LINK_MAPPING;
for(idx = 0; idx < WMM_AC_TID_NUM; idx++) {
if(protocol_cap->tid2link_ul[idx] != 0x7fff ||
protocol_cap->tid2link_dl[idx] != 0x7fff ) {
def_mapping = false;
break;
}
}
ele_pos = ele_start+3; /* start of tid2link control */
if(def_mapping) {
SET_TID2LINK_CTRL_DIRECT(ele_pos, 2);
SET_TID2LINK_CTRL_DEFAULT(ele_pos, 1);
SET_TID2LINK_CTRL_INDIC(ele_pos, indicator); /* indicator is 0 */
total_len += 2;
}
else {
/* 0: uplink */
SET_TID2LINK_CTRL_DIRECT(ele_pos, 0);
SET_TID2LINK_CTRL_DEFAULT(ele_pos, 0);
total_len += 2;
ele_linkmap = ele_pos+2;
_set_link_mapping(ele_linkmap, protocol_cap->tid2link_ul, &indicator, &total_len);
SET_TID2LINK_CTRL_INDIC(ele_pos, indicator);
ele_pos = ele_pos + total_len;
/* 1: downlink */
SET_TID2LINK_CTRL_DIRECT(ele_pos, 1);
SET_TID2LINK_CTRL_DEFAULT(ele_pos, 0);
total_len += 2;
ele_linkmap = ele_pos+2;
_set_link_mapping(ele_linkmap, protocol_cap->tid2link_dl, &indicator, &total_len);
SET_TID2LINK_CTRL_INDIC(ele_pos, indicator);
}
ele_start[1]= total_len + 1; /* Ext ID field is included */
return (total_len +3); /* From Element ID/ Len/ Ext ID */
}
void
_dump_tid2link(struct rtw_phl_stainfo_t *sta)
{
u8 idx =0;
PHL_INFO("###### _dump_tid2link #######\n");
for (idx = 0; idx < WMM_AC_TID_NUM; idx++) {
PHL_INFO("\t[TID-%d] uplink mapp:0x%02X, downlink map:%02X\n",
idx, sta->asoc_cap.tid2link_ul[idx],
sta->asoc_cap.tid2link_dl[idx]);
if(!sta->asoc_cap.tid2link_ul[idx] && !sta->asoc_cap.tid2link_dl[idx])
PHL_ERR("\t[TID-%d]:: a TID shall be mapped to at least one setup link\n",
idx);
}
}
void rtw_phl_parse_tid2link(struct rtw_phl_stainfo_t *sta,
u8 *ele_start,
u16 ele_len)
{
u8 *ele_pos = NULL;
u8 direction = 0, def_mapping = 0, indicator = 0, idx = 0;
bool downlink = true, uplink = true;
ele_pos = ele_start;
do {
direction = GET_TID2LINK_CTRL_DIRECT(ele_pos);
downlink = (direction == 0)? false: true; /* 0: uplink */
uplink = (direction == 1)? false: true; /* 1: downlink */
def_mapping = GET_TID2LINK_CTRL_DEFAULT(ele_pos);
if (!def_mapping) {
indicator = GET_TID2LINK_CTRL_INDIC(ele_pos);
ele_pos += 2; /* skip mapping control */
for(idx = 0; idx < WMM_AC_TID_NUM; idx++) {
if(BIT(idx) & indicator) {
if(uplink)
sta->asoc_cap.tid2link_ul[idx] =
GET_LINK_MAPPING_TID(ele_pos);
if(downlink)
sta->asoc_cap.tid2link_dl[idx] =
GET_LINK_MAPPING_TID(ele_pos);
ele_pos += 2;
}
}
}
else {
ele_pos += 2; /* skip mapping control */
/* 35.3.6.1.2 Default mapping mode */
for(idx = 0; idx < WMM_AC_TID_NUM; idx++) {
/* Link id 15 : if the reported AP is not part of an AP MLD. */
sta->asoc_cap.tid2link_ul[idx] = 0x7fff;
sta->asoc_cap.tid2link_dl[idx] = 0x7fff;
}
}
} while(ele_pos < (ele_start + ele_len));
_dump_tid2link(sta);
phl_mld_link2tid(sta);
}
void rtw_phl_tid2link_not_present(struct rtw_phl_stainfo_t *sta, u8 nego)
{
struct protocol_cap_t *protocol_cap = &sta->rlink->protocol_cap;
u8 idx = 0;
u16 link_ul = 0, link_dl = 0;
for(idx = 0; idx < WMM_AC_TID_NUM; idx++) {
link_ul = 0x7fff;
link_dl = 0x7fff;
/* AP accept our tid2link negotiation */
if(nego) {
link_ul = protocol_cap->tid2link_ul[idx];
link_dl = protocol_cap->tid2link_dl[idx];
}
sta->asoc_cap.tid2link_ul[idx] = link_ul;
sta->asoc_cap.tid2link_dl[idx] = link_dl;
}
phl_mld_link2tid(sta);
}
void
_dump_per_sta_profile(struct rtw_phl_per_sta_profile_element ele)
{
PHL_INFO("###### _dump_per_sta_profile #######\n");
PHL_INFO("%-25s: %d\n", "Link ID", ele.link_id);
PHL_INFO("%-25s: %s\n", "Complete Profile", (ele.complete_profile == true)?"Yes":"No");
if (ele.mac_addr_present)
PHL_INFO("%-25s: %2x:%2x:%2x:%2x:%2x:%2x\n",
"MAC address",
ele.mac_addr[0],
ele.mac_addr[1],
ele.mac_addr[2],
ele.mac_addr[3],
ele.mac_addr[4],
ele.mac_addr[5]
);
else
PHL_INFO("%-25s: Not present\n", "MAC address");
if (ele.bcn_interval_present)
PHL_INFO("%-25s: %d\n", "Beacon Interval", ele.bcn_interval);
else
PHL_INFO("%-25s: Not present\n", "Beacon Interval");
if (ele.dtim_info_present) {
PHL_INFO("%-25s: %d\n", "DTIM Count", ele.dtim_cnt);
PHL_INFO("%-25s: %d\n", "DTIM Period", ele.dtim_period);
} else {
PHL_INFO("%-25s: Not present\n", "DTIM Info");
}
if (ele.nstr_link_pair_present) {
PHL_INFO("%-25s: %d\n", "NSTR Bitmap Size", ele.nstr_bitmap_size);
PHL_INFO("%-25s: 0x%X\n", "NSTR Indication Bitmap", ele.nstr_indication_bitmap);
} else {
PHL_INFO("%-25s: Not present\n", "NSTR Link Pair");
}
if (ele.sta_profile_len != 0) {
PHL_INFO("%-25s: %d\n", "STA Profile Length", ele.sta_profile_len);
PHL_INFO("%-25s: %s\n", "STA Profile Fragment", (ele.sta_profile_frag_len == 0)?"No":"Yes");
}
}
/*
*
* +--------+--------+----------+----------+-------------+
* | Subele | Length | STA Ctrl | STA Info | STA Profile |
* | ID | | | | |
* +--------+--------+----------+----------+-------------+
* |
* |
* ele_len v
* ^
* | len_before_frag 1 1 len_before_frag - ele_len
* +--------+---+----+-----------------+-----+------+-----------------+-------------+
* | Subele | Length | Data 0 | FID | FLEN | DATA 1 |
* | ID | | | | | |
* +--------+--------------------------+-----+------+-------------------------------+
* |
* v
* ele_pos
*/
void
phl_parse_per_sta_profile_ie(struct rtw_phl_com_t *phl_com,
u8 *ele_pos,
u16 ele_len,
u8 *ele_frag,
u16 len_before_frag,
struct rtw_phl_per_sta_profile_element *ele
)
{
void *d = phlcom_to_drvpriv(phl_com);
u8 tmp[STA_CTRL_LEN + MAX_STA_INFO_LEN] = {0};
u8 *sta_ctrl = tmp;
u8 *sta_info = tmp+2;
u8 sta_info_len = 0;
u8 sta_info_offset = 0;
/* Copy STA Ctrl and STA Info field to tmp for parsing */
if (len_before_frag == 0) {
/* Per-STA profile without fragment */
sta_info_len = ele_pos[STA_CTRL_LEN];
_os_mem_cpy(d, tmp, ele_pos, (STA_CTRL_LEN + sta_info_len));
} else {
/* Per-STA profile with fragment */
if (len_before_frag <= STA_CTRL_LEN)
sta_info_len = ele_frag[STA_CTRL_LEN - len_before_frag];
else
sta_info_len = ele_pos[STA_CTRL_LEN];
if (len_before_frag < (STA_CTRL_LEN + sta_info_len)) {
_os_mem_cpy(d, tmp, ele_pos, len_before_frag);
_os_mem_cpy(d,
(tmp + len_before_frag),
ele_frag,
(STA_CTRL_LEN + sta_info_len - len_before_frag));
} else {
_os_mem_cpy(d, tmp, ele_pos, (STA_CTRL_LEN + sta_info_len));
}
}
sta_info_offset++;
ele->link_id = (u8)GET_STA_CTRL_LINK_ID(sta_ctrl);
if (GET_STA_CTRL_COMPLETE_PROFILE(sta_ctrl)) {
ele->complete_profile = true;
}
if (GET_STA_CTRL_MAC_ADDR_PRESENT(sta_ctrl)) {
ele->mac_addr_present = true;
_os_mem_cpy(d, ele->mac_addr, (sta_info+sta_info_offset), MAC_ALEN);
sta_info_offset += 6;
}
if (GET_STA_CTRL_BEACON_INTEREVAL_PRESENT(sta_ctrl)) {
ele->bcn_interval_present = true;
ele->bcn_interval = LE_BITS_TO_2BYTE(sta_info+sta_info_offset, 0, 16);
sta_info_offset += 2;
}
if (GET_STA_CTRL_DTIM_INFO_PRESENT(sta_ctrl)) {
ele->dtim_info_present = true;
ele->dtim_cnt = LE_BITS_TO_1BYTE(sta_info+sta_info_offset, 0, 8);
sta_info_offset += 1;
ele->dtim_period = LE_BITS_TO_1BYTE(sta_info+sta_info_offset, 0, 8);
sta_info_offset += 1;
}
if (GET_STA_CTRL_NSTR_LINK_PAIR_PRESENT(sta_ctrl)) {
ele->nstr_link_pair_present = true;
if (GET_STA_CTRL_NSTR_BITMAP_SIZE(sta_ctrl)) {
ele->nstr_indication_bitmap = LE_BITS_TO_2BYTE(sta_info+sta_info_offset, 0, 16);
ele->nstr_bitmap_size = 2;
sta_info_offset += 2;
} else {
ele->nstr_indication_bitmap = LE_BITS_TO_1BYTE(sta_info+sta_info_offset, 0, 8);
ele->nstr_bitmap_size = 1;
sta_info_offset += 1;
}
}
if (len_before_frag == 0) {
/* Per-STA profile without fragment */
ele->sta_profile = ele_pos + STA_CTRL_LEN + sta_info_len;
} else if (len_before_frag <= (STA_CTRL_LEN + sta_info_len)) {
/* STA profile located in fragment part */
ele->sta_profile = ele_frag + (STA_CTRL_LEN + sta_info_len - len_before_frag + 2);
} else {
/* STA profile is truncated by fragment */
ele->sta_profile = ele_pos + STA_CTRL_LEN + sta_info_len;
ele->sta_profile_frag = ele_frag + 2;
ele->sta_profile_frag_len = (u8)(len_before_frag - (STA_CTRL_LEN + sta_info_len));
}
ele->sta_profile_len = (u8)(ele_len - STA_CTRL_LEN - sta_info_len);
_dump_per_sta_profile(*ele);
}
void
_parse_ml_link_info(struct rtw_phl_com_t *phl_com,
u8 *ie_buf,
u16 ie_len,
u16 link_info_offset,
struct rtw_phl_ml_element *ml_ele
)
{
u16 offset = link_info_offset;
u8 sub_eid = 0;
u8 *sub_ele = NULL, *sub_ele_frag = NULL;
u16 sub_ele_len = 0, sub_ele_len_before_frag = 0;
u16 len_before_frag = 0;
/* Exclude the Element ID extension field */
u16 next_frag_offset = MAX_ELE_LEN - 1;
do {
if (offset >= (ie_len-1))
break;
sub_ele_frag = NULL;
sub_ele_len_before_frag = 0;
len_before_frag = 0;
/* Check fragment */
len_before_frag = next_frag_offset - offset;
if (len_before_frag == 1) {
sub_eid = ie_buf[offset];
/* Skip FID and FLEN */
sub_ele_len = ie_buf[offset+3];
sub_ele = ie_buf + offset + 4;
} else if (len_before_frag == 2){
sub_eid = ie_buf[offset];
sub_ele_len = ie_buf[offset+1];
/* Skip FID and FLEN */
sub_ele = ie_buf + offset + 4;
} else {
sub_eid = ie_buf[offset];
sub_ele_len = ie_buf[offset+1];
sub_ele = ie_buf + offset + 2;
if (len_before_frag < (sub_ele_len + 2)) {
sub_ele_frag = ie_buf + offset + len_before_frag + 2;
sub_ele_len_before_frag = len_before_frag - 2;
}
}
/*
* Next fragment offset would be
* 255: Max element length
* 1: Fragment ID field
* 1: Fragment Length field
*/
if (len_before_frag <= (sub_ele_len + 2))
next_frag_offset += (MAX_ELE_LEN + 2);
if (sub_eid == WLAN_SUBEID_PER_STA_PROFILE) {
phl_parse_per_sta_profile_ie(phl_com,
sub_ele,
sub_ele_len,
sub_ele_frag,
sub_ele_len_before_frag,
&(ml_ele->profile[ml_ele->profile_num]));
ml_ele->profile_num++;
}
/* Move offset to the end of the element */
if (len_before_frag <= (sub_ele_len + 2)) {
/*
* Subele ID field (1) +
* Subele len field (1) +
* Fragment ID field (1) +
* Fragment len field (1) +
* Subele length
*/
offset += (sub_ele_len + 2 + 2);
} else {
/*
* Subele ID field (1) +
* Subele len field (1) +
* Subele length
*/
offset += (sub_ele_len + 2);
}
} while(1);
}
void
_dump_ml_basic(struct rtw_phl_ml_element ml_ele)
{
PHL_INFO("###### _dump_ml_basic #######\n");
if (ml_ele.common_info.basic_ml.link_id_info_present)
PHL_INFO("%-25s: %d\n", "Link ID", ml_ele.common_info.basic_ml.link_id);
else
PHL_INFO("%-25s: Not present\n", "Link ID");
if (ml_ele.common_info.basic_ml.bss_param_chg_cnt_present)
PHL_INFO("%-25s: %d\n", "BSS Param Chg Cnt", ml_ele.common_info.basic_ml.bss_param_chg_cnt);
else
PHL_INFO("%-25s: Not present\n", "BSS Param Chg Cnt");
if (ml_ele.common_info.basic_ml.msd_info_present)
PHL_INFO("%-25s: Present\n", "MSD Info");
else
PHL_INFO("%-25s: Not present\n", "MSD Info");
if (ml_ele.common_info.basic_ml.eml_cap_present)
PHL_INFO("%-25s: Present\n", "EML Capability");
else
PHL_INFO("%-25s: Not present\n", "EML Capability");
if (ml_ele.common_info.basic_ml.mld_cap_present) {
PHL_INFO("%-25s =>\n", "MLD Capability");
PHL_INFO("%-25s: %d\n", "Max Num of SL",
ml_ele.common_info.basic_ml.mld_cap.max_num_sl);
PHL_INFO("%-25s: %s\n", "SRS Support",
(ml_ele.common_info.basic_ml.mld_cap.srs_support == true)?"True":"False");
if (ml_ele.common_info.basic_ml.mld_cap.tid_to_link_nego_support == 0)
PHL_INFO("%-25s: %s\n",
"TID-To-Link Mapping Nego",
"Not support");
else if (ml_ele.common_info.basic_ml.mld_cap.tid_to_link_nego_support == 1)
PHL_INFO("%-25s: %s\n",
"TID-To-Link Mapping Nego",
"Same or different link set");
else
PHL_INFO("%-25s: %s\n",
"TID-To-Link Mapping Nego",
"Same link set only");
if (ml_ele.common_info.basic_ml.mld_cap.freq_sep_for_str)
PHL_INFO("%-25s: %dMHz\n", "Freq Separation for STR",
((ml_ele.common_info.basic_ml.mld_cap.freq_sep_for_str - 1) * 80));
PHL_INFO("%-25s: %s\n", "AAR Support",
(ml_ele.common_info.basic_ml.mld_cap.aar_support == true)?"True":"False");
} else {
PHL_INFO("%-25s: Not present\n", "MLD Capability");
}
}
void
_parse_ml_basic(struct rtw_phl_com_t *phl_com,
u8 *ie_buf,
u16 ie_len,
struct rtw_phl_ml_element *ml_ele
)
{
struct basic_ml *basic_ml = &(ml_ele->common_info.basic_ml);
u8 *ml_ctrl = ie_buf;
u8 *common_info = ie_buf+2;
u8 common_info_len = 0;
u8 common_info_offset = 0;
common_info_len = common_info[0];
common_info_offset++;
basic_ml->mld_address = common_info+1;
common_info_offset += 6;
if (GET_ML_ELE_ML_CTRL_LINK_ID_INFO_PRESENT(ml_ctrl)) {
basic_ml->link_id_info_present = true;
basic_ml->link_id = LE_BITS_TO_1BYTE(common_info+common_info_offset, 0, 4);
common_info_offset += 1;
}
if (GET_ML_ELE_ML_CTRL_BSS_PARAMS_CHG_CNT_PRESENT(ml_ctrl)) {
basic_ml->bss_param_chg_cnt_present = true;
basic_ml->bss_param_chg_cnt = LE_BITS_TO_1BYTE(common_info+common_info_offset, 0, 8);
common_info_offset += 1;
}
if (GET_ML_ELE_ML_CTRL_MEDIUM_SYNC_DELAY_INFO_PRESENT(ml_ctrl)) {
basic_ml->msd_info_present = true;
/* TODO: Parse MSD INFO */
common_info_offset += 2;
}
if (GET_ML_ELE_ML_CTRL_EML_CAP_PRESENT(ml_ctrl)) {
basic_ml->eml_cap_present = true;
/* TODO: Parse EML cap */
common_info_offset += 3;
}
if (GET_ML_ELE_ML_CTRL_MLD_CAP_PRESENT(ml_ctrl)) {
basic_ml->mld_cap_present = true;
basic_ml->mld_cap.max_num_sl = GET_MLD_CAP_MAX_NUM_OF_SL(common_info+common_info_offset);
basic_ml->mld_cap.srs_support = GET_MLD_CAP_SRS_SUPPORT(common_info+common_info_offset);
basic_ml->mld_cap.tid_to_link_nego_support = GET_MLD_CAP_TID_TO_LINK_NEGO_SUPPORT(common_info+common_info_offset);
basic_ml->mld_cap.freq_sep_for_str = GET_MLD_CAP_FREQ_SEP_FOR_STR(common_info+common_info_offset);
basic_ml->mld_cap.aar_support = GET_MLD_CAP_AAR_SUPPORT(common_info+common_info_offset);
common_info_offset += 2;
}
_dump_ml_basic(*ml_ele);
_parse_ml_link_info(phl_com,
ie_buf,
ie_len,
(2 + common_info_len),
ml_ele);
}
void
_dump_ml_probe_req(struct rtw_phl_ml_element ml_ele)
{
PHL_INFO("###### _dump_ml_probe_req #######\n");
if (ml_ele.common_info.probe_req_ml.mld_id_present)
PHL_INFO("%-25s: %d\n", "MLD ID", ml_ele.common_info.probe_req_ml.mld_id);
else
PHL_INFO("%-25s: Not present\n", "MLD ID");
}
void
_parse_ml_probe_req(struct rtw_phl_com_t *phl_com,
u8 *ie_buf,
u16 ie_len,
struct rtw_phl_ml_element *ml_ele
)
{
struct probe_req_ml *probe_req_ml = &(ml_ele->common_info.probe_req_ml);
u8 *ml_ctrl = ie_buf;
u8 *common_info = ie_buf+2;
u8 common_info_len = 0;
u8 common_info_offset = 0;
common_info_len = common_info[0];
common_info_offset++;
if (GET_ML_ELE_ML_CTRL_MLD_ID_PRESENT(ml_ctrl)) {
probe_req_ml->mld_id_present = true;
probe_req_ml->mld_id = LE_BITS_TO_1BYTE(common_info+common_info_offset, 0, 8);
common_info_offset += 1;
}
_dump_ml_probe_req(*ml_ele);
_parse_ml_link_info(phl_com,
ie_buf,
ie_len,
(2 + common_info_len),
ml_ele);
}
void
rtw_phl_parse_ml_ie(struct rtw_phl_com_t *phl_com,
u8 *ele_pos,
u16 ele_len,
struct rtw_phl_ml_element *ml_ele
)
{
if ((ele_pos == NULL) || (ele_len == 0))
return;
if (ml_ele == NULL)
return;
ml_ele->type = GET_ML_ELE_ML_CTRL_TYPE(ele_pos);
if (ml_ele->type == BASIC_ML) {
_parse_ml_basic(phl_com, ele_pos, ele_len, ml_ele);
} else if (ml_ele->type == PROBE_REQUEST_ML) {
_parse_ml_probe_req(phl_com, ele_pos, ele_len, ml_ele);
} else {
PHL_WARN("Unknown type!\n");
}
}
/*
* rtw_phl_get_ie: Return the total length of the element (include the fragment
* element)
*
* Ex. Element fragmentation without Element ID Extension
* ele_len = 255 + 1 + 1 + 255 + 1 + 1 +n
* |------------------------------------------|
* | |
* v 255 1 1 255 1 1 n v
* +------------------------------------------------------+
* | EID | 255 | Data | FID | 255 | Data | FID | n | Data |
* +------------------------------------------------------+
* ^
* |
* out_ele
* Ex. Element fragmentation with Element ID Extension
* ele_len = 254 + 1 + 1 + 255 + 1 + 1 +n
* +----------------+---+---+-----+---+-------+
* v v
* 1 254 1 1 255 1 1 n
* +-----+-----+-----+------+-----+-----+------+-----+---+------+
* | EID | 255 | EXT | Data | FID | 255 | Data | FID | n | Data |
* +-----+-----+-----+------+-----+-----+------+-----+---+------+
* ^
* +
* out_ele
*/
u16
rtw_phl_get_ie(u8 *ie_start,
u16 ies_len,
u8 target_id,
u8 target_ext_id,
u8 **out_ele
)
{
u16 offset = 0, frag_offset = 0;
u16 ele_len = 0;
u8 tmp_id = 0;
u8 tmp_ext_id = 0;
u16 tmp_ele_len = 0;
u8 frag_len = 0;
bool is_fragmentable = false;
do {
if ((offset + 2) >= ies_len)
break;
/* Get current element ID */
tmp_id = ie_start[offset];
tmp_ele_len = ie_start[offset+1];
if (tmp_id == EID_EXTENSION)
tmp_ext_id = ie_start[offset+2];
is_fragmentable = rtw_phl_is_ie_fragmentable(tmp_id, tmp_ext_id);
if (is_fragmentable) {
frag_offset = offset + 2 + tmp_ele_len;
frag_len = (u8)tmp_ele_len;
while (frag_len == 255) {
/*
* Check there is more data at the end of the IE
* and is followed by fragment element
*/
if (((frag_offset + 2) < ies_len) &&
(ie_start[frag_offset] == EID_FRAGMENT)){
frag_len = ie_start[frag_offset+1];
frag_offset += (2+frag_len);
tmp_ele_len += (2+frag_len);
}
}
}
/* Check element length is valid (ele length + N * frag length) */
if ((offset + 2 + tmp_ele_len) > ies_len) {
PHL_WARN("%s: Get invalid length!\n", __func__);
return 0;
}
if (target_id == tmp_id) {
if (target_id == EID_EXTENSION) {
/* Check Extension ID */
if (target_ext_id != tmp_ext_id) {
/* Extension ID is different */
offset += (tmp_ele_len + 2);
continue;
}
/* Return pointer of the first byte after extension ID */
*out_ele = ie_start + offset + 3;
/* Return length - extension id field (1) */
ele_len = tmp_ele_len - 1;
break;
}
/* Return pointer of the first byte after lenght field */
*out_ele = ie_start + offset + 2;
ele_len = tmp_ele_len;
break;
} else {
/* Element ID is different */
offset += (tmp_ele_len + 2);
}
} while(1);
return ele_len;
}
bool
rtw_phl_is_ie_fragmentable(u32 eid,
u32 eid_ext
)
{
bool ret = false;
if (eid == EID_FILS_INDICATION)
ret = true;
else if (eid == EID_EXTENSION) {
if ((eid_ext == EID_EXT_FILS_KEY_CONFIRM) ||
(eid_ext == EID_EXT_FILS_HLP_CONTAINER) ||
(eid_ext == EID_EXT_KEY_DELIVERY) ||
(eid_ext == EID_EXT_FILS_WRAPPED_DATA) ||
(eid_ext == EID_EXT_FILS_PUBLIC_KEY) ||
(eid_ext == EID_EXT_CDMG_EXTEND_SCHEDULE) ||
(eid_ext == EID_EXT_SSW_REPORT) ||
(eid_ext == EID_EXT_SPSH_REPORT) ||
(eid_ext == EID_EXT_GAS_EXTENSION) ||
(eid_ext == EID_EXT_MULTI_LINK) ||
(eid_ext == EID_EXT_TID_TO_LINK_MAPPING))
ret = true;
}
return ret;
}
/*
* Currently, the reported APs in Reduced Neighbor Report are
* affiliated with the same MLD as the reported AP. Therefore,
* MLD ID would be 0.
*/
u8 _build_mld_parameters(void *phl,
struct rtw_wifi_role_link_t *rlink,
u8 *pbuf)
{
struct rtw_phl_stainfo_t *sta = rtw_phl_get_stainfo_self(phl, rlink);
u8 *p = pbuf;
SET_MLD_PARAMS_MLD_ID(p, 0);
SET_MLD_PARAMS_LINK_ID(p, sta->link_id); /* = rlink->id for AP mode */
SET_MLD_PARAMS_BSS_PARAMS_CHG_CNT(p, rlink->bss_params_chg_cnt);
return 3;
}
/*
* Currently, the reported APs in Reduced Neighbor Report are
* affiliated with the same MLD as the reported AP.
*/
u8 rtw_phl_build_reduced_nb_rpt(struct rtw_wifi_role_t *wrole,
struct rtw_wifi_role_link_t *rlink,
u8 *pbuf)
{
void *phl = wrole->phl_com->phl_priv;
void *drv = wrole->phl_com->drv_priv;
u8 tbtt_info_cnt;
u8 tbtt_info_len;
u8 *pstart = pbuf;
u8 *p = pbuf + 2;
struct rtw_phl_mld_t *mld = rtw_phl_get_mld_self(phl, wrole);
struct rtw_wifi_role_link_t *another_rlink;
u8 lidx;
/* only support MLD currently */
if (mld == NULL || mld->type != DEV_TYPE_MLD || wrole->rlink_num == 1)
return 0;
tbtt_info_cnt = 0;
tbtt_info_len = 16;
for (lidx = 0; lidx < wrole->rlink_num; lidx++) {
another_rlink = get_rlink(wrole, lidx);
if (another_rlink == rlink)
continue;
/* TBTT Information Header */
SET_TBTT_INFO_FIELD_TYPE(p, 0);
SET_FILTED_NB_AP(p, 0);
SET_TBTT_INFO_CNT(p, tbtt_info_cnt);
SET_TBTT_INFO_LEN(p, tbtt_info_len);
p += 2;
/* Operating Class */
*p++ = rtw_phl_get_operating_class(another_rlink->chandef);
/* Channel Number */
*p++ = another_rlink->chandef.chan;
/* TBTT Information Set */
*p++ = 0; /* Neighbor AP TBTT Offset */
_os_mem_cpy(drv, p, another_rlink->mac_addr, ETH_ALEN); /* BSSID */
p += ETH_ALEN;
p += 4; /* TODO: Short SSID */
p += 1; /* TODO: BSS Parameters */
p += 1; /* TODO: 20 MHz PSD */
p += _build_mld_parameters(phl, another_rlink, p);
}
*pstart = EID_REDUCED_NEIGHBOR_REPORT;
*(pstart + 1) = (u8)(p - pstart - 2);
return (u8)(p - pstart);
}
void _dump_reduced_nb_rpt(struct rtw_phl_rnb_rpt_element *reduced_nb_rpt)
{
struct tbtt_info_header *hdr;
struct rtw_phl_tbtt_info *tbtt_info;
u8 i;
int j;
PHL_INFO("###### _dump_reduced_nb_rpt #######\n");
for (i = 0; i < reduced_nb_rpt->nb_ap_num; i++) {
PHL_INFO("%s - %d\n", "Neighbor AP", i);
hdr = &reduced_nb_rpt->nb_aps[i].tbtt_info_hdr;
if (!hdr->is_legal) {
PHL_INFO("%-25s %d %s\n", "Length", hdr->len, "is unrecognized");
continue;
}
PHL_INFO("%-25s: %d\n", "Info Field Type", hdr->type);
PHL_INFO("%-25s: %s\n", "Filtered Neighbor AP",
(hdr->filtered_nb_ap == true ? "True" : "False"));
PHL_INFO("%-25s: %d\n", "TBTT Info Count", hdr->cnt);
PHL_INFO("%-25s: %d\n", "TBTT Info Length", hdr->len);
PHL_INFO("%-25s: %d\n", "Operating Class", reduced_nb_rpt->nb_aps[i].op_class);
PHL_INFO("%-25s: %d\n", "Channel", reduced_nb_rpt->nb_aps[i].ch);
for (j = 0; j < (hdr->cnt + 1); j++) {
tbtt_info = &reduced_nb_rpt->nb_aps[i].tbtt_infos[j];
PHL_INFO("%s - %d\n", "TBTT Info", j);
PHL_INFO("%-25s: %d\n", "Neighbor AP Offset", tbtt_info->offset);
if (hdr->bssid_is_present)
PHL_INFO("%-25s: %02x:%02x:%02x:%02x:%02x:%02x\n", "BSSID",
tbtt_info->bssid[0], tbtt_info->bssid[1],
tbtt_info->bssid[2], tbtt_info->bssid[3],
tbtt_info->bssid[4], tbtt_info->bssid[5]);
else
PHL_INFO("%-25s: Not present\n", "BSSID");
if (hdr->short_ssid_is_present)
PHL_INFO("%-25s: %s\n", "Short SSID", (char *)tbtt_info->short_ssid);
else
PHL_INFO("%-25s: Not present\n", "Short SSID");
if (hdr->bss_param_is_present) {
PHL_INFO("%-25s: Present\n", "BSS Parameters");
PHL_INFO("%-25s: %s\n", "- OCT recommended",
(tbtt_info->bss_param.oct_recomm == true ? "True" : "False"));
PHL_INFO("%-25s: %s\n", "- Same SSID",
(tbtt_info->bss_param.same_ssid == true ? "True" : "False"));
PHL_INFO("%-25s: %s\n", "- Multiple BSSID",
(tbtt_info->bss_param.multi_bssid == true ? "True" : "False"));
PHL_INFO("%-25s: %s\n", "- Transmitted BSSID",
(tbtt_info->bss_param.transmitted_bssid == true ? "True" : "False"));
PHL_INFO("%-25s: %s\n", "- 24G/5G colocated AP",
(tbtt_info->bss_param.mem_24G_5G_colated_ap == true ? "True" : "False"));
PHL_INFO("%-25s: %s\n", "- Unsolicited pbrsp active",
(tbtt_info->bss_param.unsolicited_probe_resp_act == true ? "True" : "False"));
PHL_INFO("%-25s: %s\n", "- Colocated AP",
(tbtt_info->bss_param.colated_ap == true ? "True" : "False"));
} else
PHL_INFO("%-25s: Not present\n", "BSS Parameters");
if (hdr->max_tx_pwr_is_present)
PHL_INFO("%-25s: %d\n", "20MHz PSD", tbtt_info->max_tx_pwr);
else
PHL_INFO("%-25s: Not present\n", "20MHz PSD");
if (hdr->mld_param_is_present) {
PHL_INFO("%-25s: Present\n", "MLD Parameters");
PHL_INFO("%-25s: %d\n", "- MLD ID", tbtt_info->mld_param.mld_id);
PHL_INFO("%-25s: %d\n", "- Link ID", tbtt_info->mld_param.link_id);
PHL_INFO("%-25s: %d\n", "- BSS Param Chg Cnt",
tbtt_info->mld_param.bss_params_chg_cnt);
} else
PHL_INFO("%-25s: Not present\n", "MLD Parameters");
}
}
PHL_INFO("###### _dump_reduced_nb_rpt #######\n");
}
u8 _parse_tbtt_header(struct rtw_phl_com_t *phl_com,
u8 *pos_start,
struct tbtt_info_header *hdr)
{
u8 *pos = pos_start;
hdr->type = GET_TBTT_INFO_FIELD_TYPE(pos);
hdr->filtered_nb_ap = GET_FILTED_NB_AP(pos);
hdr->cnt = GET_TBTT_INFO_CNT(pos);
hdr->len = GET_TBTT_INFO_LEN(pos);
if (hdr->len < 1 || hdr->len == 3) {
hdr->is_legal = false;
pos += hdr->len * hdr->cnt;
goto exit;
}
hdr->is_legal = true;
pos += 2;
if (hdr->len >= 7)
hdr->bssid_is_present = true;
if (hdr->len == 5 || hdr->len == 6 || hdr->len >= 11)
hdr->short_ssid_is_present = true;
if (hdr->len == 2 || hdr->len == 6 || hdr->len == 8 || hdr->len == 9 || hdr->len >= 12)
hdr->bss_param_is_present = true;
if (hdr->len == 9 || hdr->len >= 13)
hdr->max_tx_pwr_is_present = true;
if (hdr->len == 4 || hdr->len == 10 || hdr->len >= 16)
hdr->mld_param_is_present = true;
exit:
return (u8)(pos - pos_start);
}
u8 _parse_nb_info(struct rtw_phl_com_t *phl_com,
u8 *pos_start,
struct rtw_phl_neighbor_ap *nb_ap)
{
void *drv = phl_com->drv_priv;
u8 *pos = pos_start;
u8 *pos_tbtt_info;
struct tbtt_info_header *hdr = &nb_ap->tbtt_info_hdr;
struct rtw_phl_tbtt_info *tbtt_info;
int i = 0;
/* TBTT Information Header */
pos += _parse_tbtt_header(phl_com, pos, hdr);
if (!hdr->is_legal)
goto exit;
nb_ap->op_class = *pos++;
nb_ap->ch = *pos++;
/* translate to chan_def */
if (!rtw_phl_get_chandef_from_operating_class(nb_ap->ch,
nb_ap->op_class, &nb_ap->chan_def)) {
PHL_TRACE(COMP_PHL_DBG, _PHL_INFO_,
"%s: getting channel definition failed !!! \n", __func__);
}
/* TBTT Information Set */
do {
pos_tbtt_info = pos;
tbtt_info = &nb_ap->tbtt_infos[i];
/* TBTT Offset*/
tbtt_info->offset = *pos++;
/* BSSID */
if (hdr->bssid_is_present) {
_os_mem_cpy(drv, tbtt_info->bssid, pos, MAC_ALEN);
pos += MAC_ALEN;
}
/* Short SSID */
if (hdr->short_ssid_is_present) {
_os_mem_cpy(drv, tbtt_info->short_ssid, pos, 4);
pos += 4;
}
/* BSS Parameters */
if (hdr->bss_param_is_present) {
tbtt_info->bss_param.oct_recomm =
GET_BSS_PARAMS_OCT_RECOMM(pos);
tbtt_info->bss_param.same_ssid =
GET_BSS_PARAMS_SAME_SSID(pos);
tbtt_info->bss_param.multi_bssid =
GET_BSS_PARAMS_MULTI_BSSID(pos);
tbtt_info->bss_param.transmitted_bssid =
GET_BSS_PARAMS_TRANSMITTED_BSSID(pos);
tbtt_info->bss_param.mem_24G_5G_colated_ap =
GET_BSS_PARAMS_MEM_24G_5G_COLOCATED_AP(pos);
tbtt_info->bss_param.unsolicited_probe_resp_act =
GET_BSS_PARAMS_UNSOLICITED_PROBE_RESP_ACTIVE(pos);
tbtt_info->bss_param.colated_ap =
GET_BSS_PARAMS_COLOACTED_AP(pos);
pos += 1;
}
/* 20Mhz psd */
if (hdr->max_tx_pwr_is_present)
tbtt_info->max_tx_pwr = *pos++;
/* MLD parameters */
if (hdr->mld_param_is_present) {
tbtt_info->mld_param.mld_id = GET_MLD_PARAMS_MLD_ID(pos);
tbtt_info->mld_param.link_id = GET_MLD_PARAMS_LINK_ID(pos);
tbtt_info->mld_param.bss_params_chg_cnt = GET_MLD_PARAMS_BSS_PARAMS_CHG_CNT(pos);
pos += 3;
}
/* ignore the rest reserved fields */
pos = pos_tbtt_info + hdr->len;
i++;
} while (i < (hdr->cnt + 1) && i < MAX_TBTT_INFO_NUM);
exit:
return (u8)(pos - pos_start);
}
void rtw_phl_parse_reduced_nb_rpt(struct rtw_phl_com_t *phl_com,
u8 *ele_start,
u16 ele_len,
struct rtw_phl_rnb_rpt_element *reduced_nb_rpt)
{
u8 *ele_pos = NULL;
if ((ele_start == NULL) || (ele_len == 0))
return;
if (reduced_nb_rpt == NULL)
return;
ele_pos = ele_start;
do {
if (reduced_nb_rpt->nb_ap_num >= MAX_NEIGHBOR_AP_NUM)
break;
ele_pos += _parse_nb_info(phl_com, ele_pos, &reduced_nb_rpt->nb_aps[reduced_nb_rpt->nb_ap_num]);
reduced_nb_rpt->nb_ap_num++;
} while(ele_pos < (ele_start + ele_len));
}